【问题标题】:Encoding a bcrypt string with mcrypt使用 mcrypt 编码 bcrypt 字符串
【发布时间】:2015-01-27 16:45:25
【问题描述】:

使用 PHP,我创建了以下一组函数,最终接受一个字符串(密码)并对其应用 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';
}

所以正在对字符串执行某些操作...我只是不知道是什么

【问题讨论】:

  • 为什么要加密单向哈希?
  • @HoboSapiens 数据库中的所有内容都使用 mcrypt 加密。我宁愿不对不同列的前端加密进行一点混合和匹配。我的意思是我知道我不需要在 bcrypt 上执行 mcrypt,但为了保持一致性,我想为什么不这样做。
  • 很公平,尽管如果您的解密密钥存储在服务器上,则加密的价值是微不足道的。
  • 创建一个我们可以实际运行的示例怎么样?您创建了一些“不平衡”函数(即解码函数比编码函数更多或更少),很难看出实际发生了什么。
  • @owlstead 从具有 if 语句的部分开始,这是实际部分。我实际上将这些作为函数保留在代码中,并在需要时执行它们。

标签: php encryption encoding hash bcrypt


【解决方案1】:

由于某些愚蠢的原因,PHP 包含了 \0 值字符,mcrypt 在解密的明文中用作零填充。更愚蠢的是,那些似乎也包含在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;
}
?>

【讨论】:

  • 啊,我认为这与一些愚蠢的隐形角色有关。我确实同意 PHP 有时是多么平庸,尤其是对于像这样的敏感和关键函数。非常感谢您的帮助!
  • 请注意 MCRYPT_RIJNDAEL_256 不是 AES,它不能被不允许具有 256 位块大小的 Rijndael 的运行时解密。我在我为 PHP 重写的 mcrypt_encrypt 示例代码中特别提到了这一点。
  • 我注意到了那条评论。我已经意识到这一点,并确保我正在使用的运行时允许 Rijndael256。我之前已经在 mcrypt 中使用了这种模式,但感谢您的提醒 :)
猜你喜欢
  • 2012-12-12
  • 1970-01-01
  • 2011-02-09
  • 1970-01-01
  • 2013-12-29
  • 2019-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多