PHP密码加密

PHP密码加密

在CodeIgniter的文档中看到了对密码加密的一些建议:

  • 绝不要以明文存储密码。
    永远使用 哈希算法 来处理密码。
  • 绝不要使用 Base64 或其他编码方式来存储密码。
    这和以明文存储密码是一样的,使用 哈希 ,而不要使用 编码 。
    编码以及加密,都是双向的过程,而密码是保密的,应该只被它的所有者知道, 这个过程必须是单向的。哈希正是用于做这个的,从来没有解哈希这种说法, 但是编码就存在解码,加密就存在解密。
  • 绝不要使用弱哈希或已被破解的哈希算法,像 MD5 或 SHA1 。
    这些算法太老了,而且被证明存在缺陷,它们一开始就并不是为了保存密码而设计的。
    另外,绝不要自己发明算法。
    只使用强密码哈希算法,例如 BCrypt ,在 PHP 自己的 密码哈希 函数中也是使用它。
  • 绝不要以明文形式显示或发送密码。
    即使是对密码的所有者也应该这样。如果你需要 “忘记密码” 的功能,可以随机生成一个新的 一次性的(这点很重要)密码,然后把这个密码发送给用户。
  • 绝不要对用户的密码做一些没必要的限制。
    如果你使用除 BCrypt(它有最多 72 字符的限制)之外的其他哈希算法,你应该设置一个相对长一点的密码长度(例如 1024 字符),这样可以缓解 DoS 攻击。
    但是除此之外,对密码的其他限制诸如密码中只允许使用某些字符,或者密码中不允许包含某些字符,就没有任何意义了。
    这样做不仅不会提高安全性,反而 降低了 安全性,而且真的没有任何理由需要这样做。 只要你对密码进行哈希处理了,那么无论是技术上,还是在存储上都没有任何限制。

从CodeIgniter的建议中我们看到像base64_encode()、base64_decode()这种用于编码、解码的函数是不推荐用来加密的,md5()、sha1()等弱哈希算法的函数都不是过时不推荐使用的。
文档中推荐的是5.5版本后PHP核心提供的password hashing函数族:password_get_info(), password_hash(), password_need_rehash(), password_verify()

password函数族

password_hash()

string password_hash ( string $password , integer $algo [, array $options ] )

password_hash()函数使用单向强哈希算法生成一个密码哈希。password_hash()crypt()兼容,password_hash()可以使用crypt()生成的哈希。
password_hash()支持的哈希算法:

  • PASSWORD_DEFAULT
    PHP5.5.0默认使用bcrpy算法。需要注意的是使用这个算法的hash是会随着时间变化而变化的强算法,也是因为这个原因结果的长度也是会随时间变化而变化的。因此建议存储密码的数据库字段要超过60个字符,最佳选择是255个字符。
  • PASSWORD_BCRYPT
    使用CRYPT_BLOWFISH算法生成一个使用$2y$标识符的与crypt()兼容的标准哈希。哈希的长度永远是60个字符。

参数列表

  • password
    用户的密码。 注意:使用PASSWORD_BCRIPT算法时,会把password追加到最大长度70个字符。
  • algo
    指定算法。
  • option
    包含可选项的关联数组。目前支持两个选项:salt,在散列密码加的干扰字符串。cost,算法递归的层数。
    如果不指定这两个选项,会使用随机的salt和默认的cost。

返回值

  • 成功
    返回密码哈希
  • 失败
    FALSE

哈希算法,salt,cost都会作为哈希的一部分返回。 所以结果中包含了password_verify需要的所有信息。

栗子

<?php
    $str = 'abc';
    echo '<br />1st: '.password_hash($str, PASSWORD_BCRYPT);
    echo '<br />2nd: '.password_hash($str, PASSWORD_BCRYPT);
    echo '<br />3rd: '.password_hash($str, PASSWORD_BCRYPT);

这个函数至少需要两个参数。
会打印出类似下面的结果:
1st: $2y$10$.yhUghSQU9o.iBfWrtvjUOWxPlhF7SzxDr3sZFM0Pmsm6kcryWDyi
2nd:$2y$10$NZZ2lusHU67DdwmS2llSLuJ1/ynGo5NTdQsfnXoEqUp.a5Ti1FrQW
3rd:$2y$10$LB1ZzrIRAzEZiv9iHl9Yp.9WaG08h0.5/eB64w85Anrtm8fp8alsW

从上面的结果我们可以看出:
1. 每次结果都不同。
2. 三个哈希都以$2y$10$开头,其中$2y$是标识符,10$是默认的cost。

我们尝试传递cost

    <?php
        $str = 'abc';
        $options = array(
            'cost'=>12
        );
        echo password_hash($str, PASSWORD_BCRYPT, $options);

结果为:

$2y$12$F28Fhq1ITz8yyovEW1EEdelA.nsB3WvVR.12Fks.4DoKGGnp96uCC

我们可以看到cost变成了我们自定义的值。

password_get_info()

在介绍验证函数之前,我们先看password_get_info(),这个函数会返回password_hash()生成的hash的信息的数组。

array password_get_info ( string $hash )

参数列表
一个password_hash()返回的hash。

返回值
包含三个元素的关联数组:

  • algo, 算法常量。
  • algoName, 易读的算法名字。
  • 可选项,password_hash()中的可选项参数。

栗子

<?php
    if ($hash = password_hash('abc', PASSWORD_BCRYPT, array('cost'=>12,'solt'=>'c'))){
        var_dump(password_get_info($hash));
    }

//结果
array(3) { ["algo"]=> int(1) ["algoName"]=> string(6) "bcrypt" ["options"]=> array(1) { ["cost"]=> int(12) } }

password_verify()

验证一个字符和hash是否匹配。

boolean password_verify ( string $password , string $hash ) 

因为password_hash()返回了包含算法、cost、salt等信息的结果,所有password_verify()需要的信息都包含在这个结果中,所以这个函数不需要额外的参数来传递算法、cost等信息。
参数列表

  • password
    需要验证的密码
  • hash
    password_hash()的返回值。

返回值
匹配成功,TRUE。否则,FALSE。

栗子

<?php
    $opt = array(
        'cost'=>12,
        'salt'=>mcrypt_create_iv(22, MCRYPT_DEV_URANDOM)
        );
    $password1 = 'abc';
    $password2 = 'abc1';
    if ($hash = password_hash('abc', PASSWORD_BCRYPT, $opt))
    {
        if (password_verify($password1, $hash))
            echo 'password1 is valid.';
        else
            echo 'password1 is invalid.';

        if (password_verify($password2, $hash))
            echo 'password2 is valid.';
        else
            echo 'password2 is invalid.';
    }

    //结果
    password1 is valid.
    password2 is invalid.

password_needs_refresh()

检查指定的hash是否由提供的算法,可选项实现。如果不是,就认为hash需要更新。

参数列表

  • hash
    password_hash()生成的hash。
  • algo
  • options
    algo和option都与password_hash()的参数一致。

例子就不再自己写了,cp的php手册的例子。

    <?php

    $password = 'rasmuslerdorf';
    $hash = '$2y$10$YCFsG6elYca568hBi2pZ0.3LDL5wjgxct1N8w/oLR/jfHsiQwCqTS';

    // The cost parameter can change over time as hardware improves
    $options = array('cost' => 11);

    // Verify stored hash against plain-text password
    if (password_verify($password, $hash)) {
        // Check if a newer hashing algorithm is available
        // or the cost has changed
        if (password_needs_rehash($hash, PASSWORD_DEFAULT, $options)) {
            // If so, create a new hash, and replace the old one
            $newHash = password_hash($password, PASSWORD_DEFAULT, $options);
        }

        // Log user in
    }
    ?>

你可能感兴趣的:(PHP,密码)