2016-03-13 51 views
0

我知道這種類型的問題在這裏被問到超過很多次,但是在每個問題中都有建議將這些問題提交給知道他們在做什麼的人。 (所以我們可以瞭解我們在做什麼,以及)這個哈希/驗證類是否足夠安全?

我也找不到足夠令人滿意的例子有更新的PHP函數..

因此,這裏是我的一個開源開發我的散列類項目。我有4個步驟

  1. 的base64與服務器端密鑰使用SHA256
  2. 合併編碼密碼,更安全的加密格式
  3. 合併密碼,合併密碼和胡椒,與將被存儲在隨機密鑰數據庫(稱爲膽固醇,防止混淆,因爲作爲主要散鹽我使用password_hash默認隨機鹽)
  4. 哈希與password_hash結果,默認河豚,有16個成本

我不確定的部分是密鑰長度,我應該使用另一個base64編碼,以防止sha256的原始字節問題,並且生成的哈希具有直接插入到mysql的正確格式。

類的使用本身也有多安全。

這裏是類:

<?php 
namespace shotwn\lazywork; 

/** 
* add manual here 
* pepper is a static server-side key, generated with hash_hmac sha256 and random keys 
* 
* cholesterol is a random, 22digit?(need more?) database stored key which has been used as salt with 
* hash_hmac sha256 
* 
* main structure is 
* password_hash(hash_hmac(sha256, hash_hmac(sha256, base64_encode(password), pepper),cholesterol)) 
* 
*/ 

class PasswordKitchen { 
    private static $password_pepper; 

    function __construct() { 
     try { 
     self::$password_pepper = include "/../.nope/biber.key"; 
     } catch (Exception $e) { 
     throw new Exception("No pepper key"); 
     } 
    } 

    private function season(string $password, string $cholesterol = null) { 
    //use site-wide password pepper 
    $password_safe = base64_encode ($password); 
    if(isset($cholesterol) && $cholesterol != null) { 
     $password_cholesterol = $cholesterol; 
    } else { 
     $password_cholesterol = substr(base64_encode(openssl_random_pseudo_bytes(17)),0,22);; //will be user-based mysql recorded 
     $password_cholesterol = str_replace("+",".",$password_cholesterol); 
    } 

    $password_with_pepper = hash_hmac("sha256",$password_safe,self::$password_pepper); 
    $password_with_pepper_and_cholesterol = hash_hmac("sha256",$password_with_pepper,$password_cholesterol); 

    $seasonedPassword = (array) [ 
     "password_w_PaC" => $password_with_pepper_and_cholesterol, 
     "password_cholesterol" => $password_cholesterol, 
    ]; 

    return $seasonedPassword; 
    } 

    public function hash(string $password, $cost = 16) { 
    $options = [ 
     'cost' => $cost, //change for admin accounts 
    ]; 

    $seasoning = $this->season($password); 
    $seasoned_password = $seasoning["password_w_PaC"]; 
    $password_cholesterol = $seasoning["password_cholesterol"]; 

    $passwordHash = password_hash($seasoned_password, PASSWORD_DEFAULT); 

    return (array) [ 
     "hash" => $passwordHash, 
     "cholesterol" => $password_cholesterol, 
    ]; 
    } 

    public function validate(string $password, string $cholesterol, string $hash) { 
    $seasonThePassword = $this->season($password, $cholesterol); 
    return password_verify($seasonThePassword["password_w_PaC"], $hash); 
    } 
} 
+3

如果您使用密碼散列API就足夠了。步驟1-3是完全不必要的。 –

+1

@CharlotteDunois說什麼,你真的需要添加的唯一東西是檢查存儲的散列是否是最新的(PHP可以稍後改變默認算法)[password_needs_rehash](http://php.net/manual/en/ function.password-需求,rehash.php) – JimL

回答

2

這是password_hash()已經這樣做了:

  • 它會產生一個安全的鹽
  • ,並計算了成本 因素的BCrypt哈希目前爲10.

所以沒有必要採取額外的步驟t o安全地存儲您的密碼。特別是隨機鹽(膽固醇)的產生已經由功能完成。您傳遞給您的函數的成本因素從未使用過。

因此,我建議只使用password_hash()直接:

// Hash a new password for storing in the database. 
// The function automatically generates a cryptographically safe salt. 
$hashToStoreInDb = password_hash($password, PASSWORD_DEFAULT); 

// Check if the hash of the entered login password, matches the stored hash. 
// The salt and the cost factor will be extracted from $existingHashFromDb. 
$isPasswordCorrect = password_verify($password, $existingHashFromDb); 

如果你想要更高的成本因素,你可以通過它在選項中,請注意,由一個增加成本的因素,將計算時間加倍,16似乎是不必要的高因素。

如果你想包含一個服務器端的祕密,有一個更好的方式來添加它作爲胡椒。而是加密生成的散列。關於safely storing passwords,我可以在我的教程結尾找到更多解釋。