使用 password_hash 来哈希密码

使用

PHP 5.5 时引入一个给密码加密的方法,叫 password_hash。它的使用方法如下:

$passwordHash = password_hash('123456', PASSWORD_BCRYPT);

// to do with $passwordHash ...

不可逆

上面的操作是将明文密码 123456 使用 CRYPT_BLOWFISH 算法处理成一个由 60 个字符组成的字符串,类似 $2y$10$3qI4IKS6XOiisBDTTgp17eruMdcd3dDJaqaB6pQkEHR0Uk7od2A1a,这称之为「哈希值」。

经过 password_hash 方法加密得到的哈希值有个特点:不可逆——不能从这个哈希值反推出明文密码(也就是之前的 123456)。你可能傲娇了,心想我就知道你密码设定不复杂,直接用 password_hash('123456', PASSWORD_BCRYPT) 得到哈希值,再细心的和 $2y$10$3qI4IKS6XOiisBDTTgp17eruMdcd3dDJaqaB6pQkEHR0Uk7od2A1a 比对,发现一样,不就 OK 了……这也行不通,因为每次执行 password_hash('123456', PASSWORD_BCRYPT) 语句后,得到哈希值都不一样

盐值

这是因为 password_hash 再给密码做哈希之前,会先加入一个随机子串,因为加入的随机子串每次是不一样的,所以得到的哈希值自然就不一样了。这就让在不同的服务中使用同一个密码的用户,他的密码的安全性变高了。

这个随机子串就叫「盐值」,加入盐值的过程就是「加盐处理」。

password_verify

password_hash 加密的密码,可以用 password_verify 方法验证。

<?php
// $hash 值从 `password_hash()` 方法得到 
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';

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

在做用户更新密码功能时,验证旧密码就是用 password_verify 方法。

Laravel 中的应用

Laravel 中的密码保存、验证就是使用 password_hashpassword_verify 方法,不过对它们做了封装。加密密码用 bcryptHash::make,验证密码用 Hash::check。下面是它们封装原生方法的地方:

  1. bcryptHash::make 方法底层指向的都是同一个方法 make
// see: \Illuminate\Hashing\BcryptHasher
public function make($value, array $options = [])
{
    $hash = password_hash($value, PASSWORD_BCRYPT, [
        'cost' => $this->cost($options),
    ]);

    if ($hash === false) {
        throw new RuntimeException('Bcrypt hashing not supported.');
    }

    return $hash;
}
  1. Hash::check 实现形式如下:
/**
 * Check the given plain value against a hash.
 *
 * @param  string  $value
 * @param  string  $hashedValue
 * @param  array   $options
 * @return bool
 */
public function check($value, $hashedValue, array $options = [])
{
    if (strlen($hashedValue) === 0) {
        return false;
    }

    return password_verify($value, $hashedValue);
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 1年前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 7

纠正 Hash::check 实现形式如下

require 'password.php'; 这个文件里面什么内容?

3年前 评论

@mingyun 非常感谢你的纠正。

require 'password.php' 的作用应该是引入密码的,'secret-password''bad-password' 就是从里面获取的。

我看了下,这个例子并不清晰,稍后我更换一个。:)

3年前 评论

举个例子吧:

[1] > $pw = 123456
// 123456
[2] > $hashed = Hash::make($pw);
// '$2y$10$xSugoyKv765TY8DsERJ2/.mPIOwLNdM5Iw1n3x1XNVymBlHNG4cX6'
[3] > Hash::check($hashed, $pw);
// false
[4] > Hash::check($pw, $hashed);
// true
2年前 评论

写的很好,非常感谢!

2年前 评论

这个只能是密码明文和hashpw对比,如果解决明文密码传输的安全性?

7个月前 评论

这个在用password_verify验证密码的时候是如何保证和password_hash生成的salt是一样的?

5个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!