PHP password_verify() 与 Python bcrypt.hashpw()

Posted

技术标签:

【中文标题】PHP password_verify() 与 Python bcrypt.hashpw()【英文标题】:PHP password_verify() vs Python bcrypt.hashpw() 【发布时间】:2017-03-31 07:18:27 【问题描述】:

那么,就这样吧。

我已经设置了一个 [simple] php REST API,我通过 X-API-KEY 标头键接收哈希密码。这在与另一个 PHP 脚本交互时非常有用,并且该短语通过 PHP 的 password_hash() 方法进行哈希处理。但是,当我尝试通过 Python 和请求库与 API 交互时,密钥被拒绝。以下是一些示例:

PHP:

<?php
$usrid = '123456';
$dt     = new DateTime();
$secret = "secret$usrid$dt->format('Ymd')";
$hashed = password_hash($secret, PASSWORD_BCRYPT);
echo $secret."\n";
echo $hashed."\n";
echo(phpversion());
?>

Python:

#!/usr/bin/python
import bcrypt, datetime, sys
usrid = '123456' # user id
t = datetime.datetime.now().strftime('%Y%m%d')
secret = "secretusridt".format(usrid=usrid,t=t)
hashed = bcrypt.hashpw(secret, bcrypt.gensalt())
print secret
print hashed
print '%d.%d.%d' % (sys.version_info[:3])

每一个的输出如下:

PHP:
    secret12345620161116
    $2y$10$/WUBS2RkTlfcgPxvmqYRI.EkBD/CPgnpE9rYvOqweERgSwFeENUDO
    5.6.24

Python: 
    secret12345620161116
    $2b$11$9v/l6KglHiNgOybw1Y8jWeCFHiAfv.cguO1Qmc7Noe4azSluoBeHO
    2.7.11

现在,显然它们是不同的,这就是重点,但是当您将 Python 输出传递给 PHP 的 password_verify() 函数时,它会返回 False。 PHP 输出验证正常。

这里一定有一些我想念的东西,但为了我的一生,我找不到它。我尝试使用不同的盐选项但没有成功。我错过了什么?两者不兼容吗?如果这是真的,那似乎很愚蠢。

非常感谢你们聪明的互联网人。

更新

[我已经用以下 2 行测试脚本更新了脚本]

PHP: $hashed = password_hash($secret, PASSWORD_BCRYPT, ['cost'=>11]);
Python: hashed = bcrypt.hashpw(secret, bcrypt.gensalt(11))

我已经使用这个 [PHP] 来验证上述内容:

<?php
$secret = 'secret12345620161116';

$php    = '$2y$11$rMqK7PhWtYd3E6yqqor0K.p2XEOJqbxJSrknLLWfhqZKsbYRa1YRa'; // output from php script
$python = '$2b$11$yWzCNB4dfIIVH2FLWWEQ/efSmN/KlVmLq.MGJ54plgedE1OSQgvPu'; // putput from python script

$php_needs_rehash    = password_needs_rehash($php, PASSWORD_BCRYPT);
$python_needs_rehash = password_needs_rehash($python, PASSWORD_BCRYPT);

echo 'php_needs_rehash: '.$php_needs_rehash."\n";
echo 'python_needs_rehash: '.$python_needs_rehash."\n";
echo "\n";

echo "php_info:\n";
print_r(password_get_info($php));
echo "\n";

echo "python_info:\n";
print_r(password_get_info($python));
echo "\n";

echo "php_verified: ".password_verify($secret, $php)."\n";
echo "python_verified: ".password_verify($secret, $python)."\n";
echo "\n";
?>

输出如下:

php_needs_rehash: 1
python_needs_rehash: 1

php_info:
Array
(
    [algo] => 1
    [algoName] => bcrypt
    [options] => Array
        (
            [cost] => 11
        )

)

python_info:
Array
(
    [algo] => 0
    [algoName] => unknown
    [options] => Array
        (
        )

)

php_verified: 1
python_verified: 1

所以,现在我真的很困惑,因为服务器仍然无法识别我的 python 散列密钥,如果我不按照 cmets 中richardhsu 的建议将“$2b”替换为“$2y”,那就是.

【问题讨论】:

如果在哈希之前用PHP和Python输出secret,它们是一样的吗? PHP 使用$2y$ 作为“bcrypt”算法标识符。显然 python 使用了不同的标识符。此外,下一个值是成本。不同的成本意味着哈希将重复的不同周期数。尝试将 php 成本更改为11,就像 python 哈希(可以设置为 password_hash 的选项之一),看看它是否与减去算法的 python 哈希一致。 看来可以互换***.com/questions/21329871/python-and-php-bcrypt 不同的成本应该没有关系,对吧? password_verify() 看到成本并相应地对其进行验证..您到底在比较什么? 但是哈希值永远不会相等,无论如何?我的意思是,你不能比较两个散列来查看它们是否相等(因为相同的字符串每次都会产生不同的散列)。您只能将原始值与哈希进行比较,然后它会查看哈希并从中获取成本。 【参考方案1】:

从技术上讲,它们都是 bcrypt 或 crypt-blowfish 的不同版本

在 php 中,前缀是 $2y$10$ 在 python 中,前缀是 $2b$11$

这意味着成本因素分别略有不同 10 和 11 在您的更新中,您已将成本因子固定为 11

前缀的另一部分表示 php 使用 CRYPT_BLOWFISH 散列,而 python 使用基于 Blowfish 密码的 bcrypt。

由于这些差异,两个密码不可互换。

【讨论】:

BCrypt 算法基于河豚密码,但两个平台使用相同的算法 BCrypt。不同的成本因素不会使哈希不兼容,BCrypt 的设计允许使用不同的成本因素进行验证。

以上是关于PHP password_verify() 与 Python bcrypt.hashpw()的主要内容,如果未能解决你的问题,请参考以下文章

PHP PDO MySQL password_verify问题

使用 PHP 5.5 的 password_hash 和 password_verify 函数

php password_verify 不适用于数据库 [重复]

password_verify 不验证哈希

password_verify不验证哈希

为啥 password_verify 返回 false?