为啥 PHP 的 hash_equals() 函数中的参数顺序很重要?

Posted

技术标签:

【中文标题】为啥 PHP 的 hash_equals() 函数中的参数顺序很重要?【英文标题】:Why is order of arguments in PHP's hash_equals() function important?为什么 PHP 的 hash_equals() 函数中的参数顺序很重要? 【发布时间】:2015-03-10 18:52:33 【问题描述】:

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 文档中的这个注释是对未来版本中实现更改的“保留”?


编辑

正如Morpfh 所说,initial proposal implementation 不同:

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);

如您所见,草案实现尝试处理不同长度的哈希,并且不对称地处理参​​数。也许这个实施草案不是第一个。

总结: 文档中关于参数顺序的注释似乎是实施草案的遗留物。

【问题讨论】:

当文档告诉你这样的事情并且没有解释原因时,我总是讨厌它。 1) 这是一个 PHP 问题,为什么用 'c' 标记 2) 参数必须按特定顺序排列,因为这是函数期望参数的方式。 @user3629249, 1) 它被标记为 C 因为 PHP 是用 C 实现的,它更多的是关于 PHP 的实现而不是使用。 2) 抱歉,我无法在您的最后陈述中找到任何信息。 合约应该按价值计算。尊重它。 @user2864740:+1,我愿意!这个问题没有实用价值,纯粹是好奇。 【参考方案1】:

更新:

请参阅 Rouven Weßling 的评论(在此答案下方)。


这更多的是猜测而不是答案,但也许你从中得到了一些东西。


正如你所提到的,一个猜测是,如果函数将来发生变化,无论出于何种原因,它可能是为了向后兼容,以 (1) not 在相等长度上返回 false;因此容易受到泄漏长度信息的影响 - 或 (2) 其他算法/检查需要知道哪个是哪个 - 或 (n) ...


一个可能的候选者是它是从提案到实施的剩余部分:

Request for Comments: Timing attack safe string comparison function 版本:1.0

因此,从提案中可以得到:

用户必须注意,因为用户提供的字符串(或该字符串的哈希)用作第二个参数而不是第一个参数很重要。

自提案创建以来一直存在:

Request for Comments: Timing attack safe string comparison function 版本:0.1

可以链接到References,例如:

Symfony2 constant-time string comparison(注:从 2013 年开始提交。)

其中一个返回相等的长度,而是循环使用useLen。

【讨论】:

可能你是对的,它是早期实现(或其文档)的遗留物。我没想到要研究历史。提案实现(您提供了链接)确实比较了不同长度的字符串,并且在参数处理中是不对称的。 RFC 和补丁作者在这里。你是完全正确的。我最初的想法是尽量避免泄漏长度。我将参数名称保留为前向兼容(请参阅marc.info/?l=php-internals&m=139318035405396&w=2)另外考虑一下 security.stackexchange.com 上的 question,这是 PHP 实现讨论的一部分。

以上是关于为啥 PHP 的 hash_equals() 函数中的参数顺序很重要?的主要内容,如果未能解决你的问题,请参考以下文章

PHP密码和token

PHP-密码和token

PHP-密码和token

php。为啥函数的运行时间很大?

为啥以及如何在 PHP 中使用匿名函数?

为啥调用未定义函数时没有 PHP 错误?