这个散列/验证类是否足够安全?

时间:2016-03-13 20:31:26

标签: php hash static hmac password-hash

我知道这里的问题已经在很多次上被问到了,但是在每个问题下都有建议将这些事情告诉知道自己在做什么的人。 (所以我们也可以了解我们在做什么)

此外,我无法通过更新的php函数找到足够令人满意的示例..

所以这是我正在为一个开源项目开发的哈希类。我有4个步骤

  1. base64编码密码,以获得更安全的加密格式
  2. 使用sha256
  3. 将密码与服务器端密钥合并
  4. 合并,合并密码和胡椒,随机密钥将存储在数据库中(称为胆固醇,用于防止混淆,因为作为主哈希盐我使用password_hash默认随机盐)
  5. 使用password_hash哈希结果,默认河豚,16费用
  6. 我不确定的部分是密钥长度我应该使用另一个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);
      }
    }
    

1 个答案:

答案 0 :(得分:2)

这就是password_hash()已经执行的操作:

  • 它产生安全盐
  • 并计算带有成本的BCrypt哈希值 目前因素为10。

因此无需采取额外步骤来安全存储您的密码。尤其是随机盐(胆固醇)的产生已经由该功能完成。您传递给函数的成本因素从未使用过。

所以我建议直接使用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的进一步说明。