簡體   English   中英

安全的密碼生成和存儲

[英]Secure password generation and storage

我正在構建一個登錄系統,並且我想確保自己正在編寫寫代碼以在數據庫中生成和存儲密碼。 $options['passwd']是用戶選擇作為密碼的字符串。

這是我生成哈希的代碼:

public function createPasswd($options) {
    $hash_seed = 'm9238asdasdasdad31d2131231231230132k32ka¡2da12sm2382329';
    $password = $options['passwd'];
    $date =  new DateTime();
    $timestamp = $date->getTimestamp();
    $rand_number = rand($timestamp,$timestamp + pow(91239193912939123912,3));
    $rand = pow($rand_number, 12);
    $salt = str_shuffle($rand.$hash_seed);
    $hash = crypt($password, $salt);
    return $hash;
}//End class createPasswd

我只是將哈希存儲在數據庫中,然后將其與用戶密碼進行比較,如下所示:

if ($hash == crypt($password, $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}

這足夠強大嗎? 我是否錯過了一些大問題?

好吧,讓我們來看看:

您正在當前時間戳記(≥1393631056)和7.59E + 59之間生成一個數字:

$rand_number = rand($timestamp,$timestamp + pow(91239193912939123912,3));
$rand = pow($rand_number, 12);

這些值和計算似乎只是任意的。 坦白說,如果隨機值已經很大,則pow($rand_number, 12)返回INF

然后放入隨機數和固定種子,對其進行隨機處理,然后將其與crypt一起用作鹽來哈希密碼:

$salt = str_shuffle($rand.$hash_seed);
$hash = crypt($password, $salt);

隨機浮點數加固定鹽僅在16個(對於INF )與20個不同字符之間產生。 但是,仔細查看crypt發現,如果您不指定算法, crypt將會使用CRYPT_STD_DES

基於DES的標准哈希,帶有字母“ ./0-9A-Za-z ”的兩個字符的鹽。 在salt中使用無效字符將導致crypt()失敗。

現在,這些句子中有兩個方面應該使您感到懷疑:

  1. 基於標准DES的鹽僅使用兩個字符,並且
  2. 這些字符必須來自./0-9A-Za-z ,否則crypt失敗。

因此,您的鹽不僅包含./0-9A-Za-z以外的其他./0-9A-Za-z ,而且在最壞的16個字符./0-9A-Za-z ,只有16 ^ 2 = 256種可能的鹽。

那你該怎么辦? 只是不要嘗試重新發明輪子,而是使用現有的和成熟的解決方案。

這足夠強大嗎? 我錯過了一些大問題嗎?

現代PHP實際上提供了內置的非常好的密碼哈希-BCrypt,這是三款(SCrypt,BCrypt,PBKDF2)一致推薦的密碼哈希函數之一(截至2014年初)。 更好的是,它可以為您處理鹽分(鹽應該是隨機且足夠長的)。

如果您使用的是PHP 5.5或更高版本,請閱讀PHP.net上的“安全密碼哈希” -這是在PHP中存儲密碼的常見問題解答。 如果您使用的是PHP 5.3.7,但尚未使用5.5,則可以使用password_compat庫來使用這些功能。

這些功能使用BCrypt並為您處理鹽,所以很簡單!

特別是,您可以以足夠高的成本對密碼進行哈希處理(選擇一個花費的時間足夠長,以至於在預期的最大負載下,您的站點將不受CPU限制)-就像password_hash示例2一樣

<?php
/**
 * In this case, we want to increase the default cost for BCRYPT to 12.
 * Note that we also switched to BCRYPT, which will always be 60 characters.
 */
$options = [
    'cost' => 12,
];
echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."\n";
?>

然后,您存儲它返回的字符串。

為了進行驗證,請從存儲它的位置(即數據庫)檢索返回的字符串,並與password_verify示例進行比較:

<?php
// See the password_hash() example to see where this came from.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';

if (password_verify('rasmuslerdorf', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}
?>

與往常一樣,如果您需要詳細信息,請閱讀如何安全地哈希密碼? -但是PHP 5.5密碼功能使用Bcrypt,因此您可以使用足夠高的成本。

請注意,隨着時間的流逝,您購買了更好的硬件,應該增加匹配的成本。 您可以通過以下方式透明地執行此操作:在以舊費用驗證密碼之后,檢查舊費用,如果找到舊費用,則在登錄過程中以新費用重新哈希它,從而可以提高存儲密碼的安全性而無需打擾您的用戶。 當然,從未登錄的用戶不會從中受益。

對於舊的5.3.7之前的crypt()示例,請參見如何在PHP中使用bcrypt哈希密碼的主要答案 ,其getSalt函數為:

  private function getSalt() {
    $salt = sprintf('$2a$%02d$', $this->rounds);

    $bytes = $this->getRandomBytes(16);

    $salt .= $this->encodeBytes($bytes);

    return $salt;
  }

更長的鹽並不意味着更好的保護。 您沒有正確使用crypt函數。 $ salt參數不應為簡單的隨機字符串。

考慮這個例子:

echo crypt('password one', 'salt lorem ipsum dolor sit amet');  
echo crypt('password two', 'salt');

兩者都將返回相同的字符串! (sa3tHJ3 / KuYvI)

檢查http://php.net/crypt以獲得有關如何正確使用$ salt的更多信息。

保留唯一的hash_seed代碼端,然后僅將結合了密碼和您的hash_seed的字符串的sha哈希(或其他算法)存儲在數據庫中,這也更好(更安全)。

正確的實現是:

define('SECRET_KEY', 'm9238asdasdasdad31d2131231231230132k32ka¡2da12sm2382329');  // longer is better

public function createPasswd($options) {
    return hash('sha256', SECRET_KEY . $options['passwd']);
}

要檢查密碼:

if ($stored_hash == hash('sha256', SECRET_KEY . $password) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}

sha256可以用系統上任何可用的算法替換。 通過以下方式獲取完整列表:

var_dump(hash_algos());

我敢肯定,你做的太過分了。

如果您使用類似的東西就足夠了

$salt = get_random_salt(); // will return any random string
$hash = md5(md5($salt).md5($user_password)); // or any combinations of hashing and concatenations. It's your secure alorithm. Or use other more resistant hash algorithm.

比您需要將$hash$salt保存到DB並以相同的方式進行檢查:

$hash = md5(md5($salt_from_db).md5($user_password));
if ($hash == $hash_from_db) {
// success
} else {
// fail
}

同樣在您的示例中,您每次都在執行pow(91239193912939123912,3)操作,但每次都會返回相同的結果。 因此,如果只添加預先計算的值,則是相同的。

我的意思是沒有必要使用那種復雜的salt算法。 您可以僅將mt_rand()用於此目的。

另請參見此處: PHP密碼的安全哈希和鹽

在網上沖浪時,我發現了一種使用河豚的更好解決方案。 通過這種方法,我知道我只需要將哈希存儲在數據庫中,而不是鹽。 顯然,越圓越多,密碼越強,生成速度越慢。

public function better_crypt($input, $rounds = 8)  {
        $salt = "";
        $salt_chars = array_merge(range('A','Z'), range('a','z'), range(0,9));
        for($i=0; $i < 22; $i++) {
          $salt .= $salt_chars[array_rand($salt_chars)];
        }
        return crypt($input, sprintf('$2a$%02d$', $rounds) . $salt);
    }//End function

if(crypt($pass_string, $passwd) == $passwd) {
   echo 'password is correct';
  }
}

使用password_hash()默認情況下在后台使用bcrypt: http : //ie2.php.net/password_hash

如果您無法從PHP版本中使用它,請訪問: https : //github.com/ircmaxell/password_compat

如果您還有其他用途,請嘗試自己編寫,現在停止,請不要聽白痴建議其他任何選擇。 特別是當他們建議使用MD5時...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM