password_verify - php sha512 salt




你如何在PHP中使用bcrypt來哈希密碼? (6)

我時不時聽到“使用bcrypt存儲PHP密碼,bcrypt規則”的建議。

但是什麼是bcrypt ? PHP不提供任何這樣的功能,維基百科關於文件加密實用程序的喋喋不休,Web搜索只是揭示了幾種不同語言的Blowfish實現。 現在Blowfish也可以通過mcrypt以PHP方式獲得,但這對於存儲密碼有什麼幫助? 河豚是一種通用密碼,它有兩種工作方式。 如果它可以被加密,它可以被解密。 密碼需要單向散列函數。

什麼是解釋?


PHP 5.5版本將內置支持BCrypt,函數password_hash()password_verify() 。 實際上,這些只是函數crypt()包裝,並且使它更容易正確使用它。 它負責生成安全的隨機鹽,並提供良好的默認值。

使用這個功能最簡單的方法是:

$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
$isPasswordCorrect = password_verify($password, $existingHashFromDb);

此代碼將使用BCrypt(算法2y )對密碼進行哈希處理,從OS隨機源生成一個隨機鹽,並使用默認成本參數(此時為10)。 第二行檢查用戶輸入的密碼是否與已存儲的散列值匹配。

如果要更改成本參數,可以這樣做,將成本參數增加1,將計算散列值所需的時間加倍:

$hash = password_hash($password, PASSWORD_BCRYPT, array("cost" => 11));

"cost"參數相反,最好省略"salt"參數,因為該功能已經盡最大努力創建密碼安全的鹽。

對於PHP 5.3.7和更高版本,存在一個兼容包 ,來自創建password_hash()函數的相同作者。 對於5.3.7之前的PHP版本,不支持2y crypt() ,unicode安全的BCrypt算法。 人們可以用2a代替它,這是早期PHP版本的最佳選擇。


bcrypt是一種散列算法,可通過硬件進行擴展(通過可配置的循環次數)。 其緩慢和多輪確保攻擊者必須部署大量資金和硬件才能破解密碼。 添加到每個密碼saltsbcrypt需要鹽),你可以肯定,沒有任何可笑的資金或硬件,攻擊幾乎是不可行的。

bcrypt使用Eksblowfish算法來散列密碼。 雖然EksblowfishBlowfish的加密階段完全相同,但Eksblowfish的關鍵調度階段確保任何後續狀態都依賴於鹽和密鑰(用戶密碼),並且在沒有兩者都知道的情況下不能預先計算狀態。 由於這個關鍵差異, bcrypt是一種單向哈希算法。 如果不知道鹽,圓和密碼 (密碼),則無法檢索純文本密碼。 [ Source ]

如何使用bcrypt:

使用PHP> = 5.5-DEV

密碼散列函數現在已直接構建到PHP> = 5.5中 。 您現在可以使用password_hash()創建任何密碼的bcrypt散列:

<?php
// Usage 1:
echo password_hash('rasmuslerdorf', PASSWORD_DEFAULT)."\n";
// $2y$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// For example:
// $2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a

// Usage 2:
$options = [
  'cost' => 11
];
echo password_hash('rasmuslerdorf', PASSWORD_BCRYPT, $options)."\n";
// $2y$11$6DP.V0nO7YI3iSki4qog6OQI5eiO6Jnjsqg7vdnb.JgGIsxniOn4C

要根據現有散列驗證用戶提供的密碼,可以使用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.3.7,<5.5-DEV(也是RedHat PHP> = 5.3.3)

GitHub有一個兼容庫 ,它基於上面用C編寫的函數的源代碼,它提供了相同的功能。 安裝兼容性庫後,用法與上述相同(如果仍在5.3.x分支上,則減去速記數組表示法)。

使用PHP <5.3.7 (DEPRECATED)

您可以使用crypt()函數來生成輸入字符串的bcrypt散列。 這個類可以自動生成salt並根據輸入驗證現有的散列。 如果您使用的PHP版本高於或等於5.3.7,強烈建議您使用內置函數或compat庫 。 此替代方案僅用於歷史目的。

class Bcrypt{
  private $rounds;

  public function __construct($rounds = 12) {
    if (CRYPT_BLOWFISH != 1) {
      throw new Exception("bcrypt not supported in this installation. See http://php.net/crypt");
    }

    $this->rounds = $rounds;
  }

  public function hash($input){
    $hash = crypt($input, $this->getSalt());

    if (strlen($hash) > 13)
      return $hash;

    return false;
  }

  public function verify($input, $existingHash){
    $hash = crypt($input, $existingHash);

    return $hash === $existingHash;
  }

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

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

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

    return $salt;
  }

  private $randomState;
  private function getRandomBytes($count){
    $bytes = '';

    if (function_exists('openssl_random_pseudo_bytes') &&
        (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) { // OpenSSL is slow on Windows
      $bytes = openssl_random_pseudo_bytes($count);
    }

    if ($bytes === '' && is_readable('/dev/urandom') &&
       ($hRand = @fopen('/dev/urandom', 'rb')) !== FALSE) {
      $bytes = fread($hRand, $count);
      fclose($hRand);
    }

    if (strlen($bytes) < $count) {
      $bytes = '';

      if ($this->randomState === null) {
        $this->randomState = microtime();
        if (function_exists('getmypid')) {
          $this->randomState .= getmypid();
        }
      }

      for ($i = 0; $i < $count; $i += 16) {
        $this->randomState = md5(microtime() . $this->randomState);

        if (PHP_VERSION >= '5') {
          $bytes .= md5($this->randomState, true);
        } else {
          $bytes .= pack('H*', md5($this->randomState));
        }
      }

      $bytes = substr($bytes, 0, $count);
    }

    return $bytes;
  }

  private function encodeBytes($input){
    // The following is code from the PHP Password Hashing Framework
    $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

    $output = '';
    $i = 0;
    do {
      $c1 = ord($input[$i++]);
      $output .= $itoa64[$c1 >> 2];
      $c1 = ($c1 & 0x03) << 4;
      if ($i >= 16) {
        $output .= $itoa64[$c1];
        break;
      }

      $c2 = ord($input[$i++]);
      $c1 |= $c2 >> 4;
      $output .= $itoa64[$c1];
      $c1 = ($c2 & 0x0f) << 2;

      $c2 = ord($input[$i++]);
      $c1 |= $c2 >> 6;
      $output .= $itoa64[$c1];
      $output .= $itoa64[$c2 & 0x3f];
    } while (true);

    return $output;
  }
}

你可以使用這樣的代碼:

$bcrypt = new Bcrypt(15);

$hash = $bcrypt->hash('password');
$isGood = $bcrypt->verify('password', $hash);

或者,您也可以使用Portable PHP Hashing Framework


對於OAuth 2密碼:

$bcrypt = new \Zend\Crypt\Password\Bcrypt;
$bcrypt->create("youpasswordhere", 10)

您可以使用PHP的crypt()函數並使用合適的Blowfish鹽傳遞bcrypt來創建單向哈希。 整個方程中最重要的是A)算法沒有受到損害,B) 你正確地對每個密碼進行加密 。 不要使用應用程序範圍內的鹽; 這將打開整個應用程序,從一組Rainbow表中進行攻擊。

PHP - Crypt函數


目前的想法:哈希應該是最慢的可用,而不是最快的可能。 這可以抑制彩虹表攻擊。

同樣相關,但是預防措施:攻擊者永遠不應無限制地訪問您的登錄屏幕。 為了防止這種情況:建立一個IP地址跟踪表,記錄每個命中以及URI。 如果超過5次嘗試登錄來自同一個IP地址,在任何五分鐘的時間內阻止並解釋。 第二種方法是像銀行那樣擁有雙層密碼方案。 在第二回合失敗時進行鎖定可以提高安全性。

總結:使用耗時的哈希函數減緩攻擊者的速度。 此外,阻止對登錄名的訪問過多,並添加第二個密碼層。


編輯:2013.01.15 - 如果您的服務器將支持它,請改用martinstoeckli的解決方案 。

每個人都想讓它變得更加複雜。 crypt()函數完成大部分工作。

function blowfishCrypt($password,$cost)
{
    $chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    $salt=sprintf('$2y$%02d$',$cost);
//For PHP < PHP 5.3.7 use this instead
//    $salt=sprintf('$2a$%02d$',$cost);
    //Create a 22 character salt -edit- 2013.01.15 - replaced rand with mt_rand
    mt_srand();
    for($i=0;$i<22;$i++) $salt.=$chars[mt_rand(0,63)];
    return crypt($password,$salt);
}

例:

$hash=blowfishCrypt('password',10); //This creates the hash
$hash=blowfishCrypt('password',12); //This creates a more secure hash
if(crypt('password',$hash)==$hash){ /*ok*/ } //This checks a password

我知道這應該是顯而易見的,但請不要使用“密碼”作為您的密碼。







bcrypt