PHP 5.6引入了hash_equals()
函数,用于安全比较密码哈希,并防止时间攻击。其签名为:
bool hash_equals(string $known_string, string $user_string)
根据文档所述,$known_string
和$user_string
必须具有相同的长度,才能有效防止时序攻击(否则将立即返回false
,泄露已知字符串的长度)。此外,文档指出:
“提供用户提供的字符串作为第二个参数而不是第一个参数非常重要。”
对我来说,这个函数在其参数中不是对称的似乎不太直观。
问题是: 为什么用户提供的字符串必须最后提供? 以下是该函数源代码的摘录:
PHP_FUNCTION(hash_equals)
{
/* ... */
if (Z_STRLEN_P(known_zval) != Z_STRLEN_P(user_zval)) {
RETURN_FALSE;
}
/* ... */
/* This is security sensitive code. Do not optimize this for speed. */
for (j = 0; j < Z_STRLEN_P(known_zval); j++) {
result |= known_str[j] ^ user_str[j];
}
RETURN_BOOL(0 == result);
}
就我而言,对于这两个参数的实现是完全对称的。唯一可能有所不同的操作是XOR运算符。
XOR运算符是否可能在非常数时间内执行,取决于参数值?它的执行时间是否取决于参数的顺序(例如,如果第一个参数为零)?
或者,PHP文档中的这个注释是对将来版本实现更改的“保留”?
编辑
PHP_FUNCTION(hash_compare)
{
/* ... */
/**
* If known_string has a length of 0 we set the length to 1,
* this will cause us to compare all bytes of userString with the null byte which fails
*/
mod_len = MAX(known_len, 1);
/* This is security sensitive code. Do not optimize this for speed. */
result = known_len - user_len;
for (j = 0; j < user_len; j++) {
result |= known_str[j % mod_len] ^ user_str[j];
}
RETURN_BOOL(0 == result);
}
正如你看到的,这个草案实现尝试处理不同长度的哈希,并且对参数进行了不对称处理。也许这个草案实现不是第一个版本。
总结:文档中关于参数顺序的注释似乎是从早期草案实现中遗留下来的。