使用PHP,我创建了以下一组函数,witch最终获取一个字符串(密码)并对其应用bcrypt加密。此外,它会生成一个与 mcrypt 一起使用的密钥,然后将其应用于 bcrypt 字符串(以及 base64 以简化字符串),然后插入到数据库中进行存储。
在解码时,我从中解密应用于哈希的 mcrypt 加密,然后使用 password_verify()
对其进行验证。
但是,如果哈希已通过 mcrypt 进程运行,我也无法password_verify()
验证哈希,即使在解码后,两个字符串(一个来自编码函数,一个来自解码)是相同的。
编码函数如下所示:
function passwordEncode($string) {
$hash = password_hash($string, PASSWORD_BCRYPT, ['cost' => 12]);
$key = generateKey();
$encrypt = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key."******", $hash, MCRYPT_MODE_ECB));
return [$encrypt, $key, $hash];
}
这将返回:
[0] ENCRYPT: lTzVGcAY1jkuawebFG/9ZI4O5f/+4hjZHRewstOBAAJwQlYydLJ+B+2QHg9A16qjCUe7FHfTacPzmvH+xnT4rQ==
[1] KEY: 122593420654793b0ee4efc932
[2] HASH: $2y$10$k/4gM1jMIMxnmfBMgrML6enMgqIvnZp2EzPU.G64P3Bb3MDrwJj8e
HASH 索引仅用于调试目的,以提供尚未通过 mcrypt 进程运行的输出哈希
解码函数如下所示:
function passwordDecode($string, $key) {
$decrypt = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key."******", base64_decode($string), MCRYPT_MODE_ECB);
return $decrypt;
}
这将返回:
DECRYPT: $2y$10$k/4gM1jMIMxnmfBMgrML6enMgqIvnZp2EzPU.G64P3Bb3MDrwJj8e
使用尚未通过 mcrypt 运行的原始哈希返回Verified
$encode = passwordEncode("password");
if(password_verify("password", $encode[2])) {
echo 'Verified';
} else {
echo 'Not verified';
}
但是,使用通过 mcrypt 加密和解密运行的哈希会返回Not verified
$encode = passwordEncode("password");
if(password_verify("password", passwordDecode($encode[0], $encode[1]))) {
echo 'Verified';
} else {
echo 'Not verified';
}
在花了几个小时基本上将我的额头磨在奶酪刨丝器上之后,我仍然无法弄清楚 mcrypt 对字符串做了什么来取消验证它。我已经尝试搜索不可见的字符(关键字尝试),但除此之外,我对原因是什么一无所知。
编辑:此外,这返回未验证
$encode = passwordEncode("password");
if($encode[2]==passwordDecode($encode[0], $encode[1])) {
echo 'Verified';
} else {
echo 'Not verified';
}
所以正在对字符串执行某些操作...我只是不知道是什么
出于某种愚蠢的原因,PHP 在解密的明文中包含 mcrypt 用作零填充的'0
值字符。更愚蠢的是,这些似乎也包含在password_verify
执行的base64解码中,这使得它在没有明确原因的情况下失败。这种愚蠢使PHP成为用于安全相关功能的最糟糕的环境之一。
因此,事不宜迟,执行的重写函数 rtrim
,在一段可以自行运行的代码中。需要 PHP 5.5 或 password_compat :
<?php
# uncomment for PHP 5.3/5.4
# require "lib/password.php";
function generateKey() {
$fp = @fopen('/dev/urandom','rb');
if ($fp !== FALSE) {
$key = @fread($fp, 16);
@fclose($fp);
return $key;
}
return null;
}
function hashPassword($password) {
$hash = password_hash($password, PASSWORD_BCRYPT, array('cost' => 12));
return $hash;
}
function encryptHash($key, $hash) {
# encrypt using unsafe ECB mode and without AES
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $hash, MCRYPT_MODE_ECB);
$encoded = base64_encode($encrypted);
return $encoded;
}
function decryptHash($key, $ciphertext) {
$decoded = base64_decode($ciphertext);
$decryptedHash = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $decoded, MCRYPT_MODE_ECB);
# remove stupid zero padding
$decryptedHash = rtrim($decryptedHash, "'0");
return $decryptedHash;
}
$hash = hashPassword("password");
if(password_verify("password", $hash)) {
echo 'Verified' . PHP_EOL;
} else {
echo 'Not verified' . PHP_EOL;
}
$key = generateKey();
$encrypted = encryptHash($key, $hash);
$decrypted = decryptHash($key, $encrypted);
if(password_verify('password', $decrypted)) {
echo 'Verified' . PHP_EOL;
} else {
echo 'Not verified' . PHP_EOL;
}
?>