使用 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_hash
和 password_verify
方法,不过对它们做了封装。加密密码用 bcrypt
和 Hash::make
,验证密码用 Hash::check
。下面是它们封装原生方法的地方:
bcrypt
和Hash::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;
}
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 协议》,转载必须注明作者和本文链接
纠正
Hash::check 实现形式如下
require 'password.php'; 这个文件里面什么内容?
@mingyun 非常感谢你的纠正。
require 'password.php'
的作用应该是引入密码的,'secret-password'
和'bad-password'
就是从里面获取的。我看了下,这个例子并不清晰,稍后我更换一个。:)
举个例子吧:
写的很好,非常感谢!
@Smile_Ming :blush:
这个只能是密码明文和hashpw对比,如果解决明文密码传输的安全性?
这个在用password_verify验证密码的时候是如何保证和password_hash生成的salt是一样的?