我刚刚在Zend Framework的HTTP Auth库中遇到过这段代码。它似乎使用特殊的字符串比较功能,使其更安全。但是,我不太明白这些评论。任何人都可以解释为什么这个函数比执行$a == $b
更安全吗?
/**
* Securely compare two strings for equality while avoided C level memcmp()
* optimisations capable of leaking timing information useful to an attacker
* attempting to iteratively guess the unknown string (e.g. password) being
* compared against.
*
* @param string $a
* @param string $b
* @return bool
*/
protected function _secureStringCompare($a, $b)
{
if (strlen($a) !== strlen($b)) {
return false;
}
$result = 0;
for ($i = 0; $i < strlen($a); $i++) {
$result |= ord($a[$i]) ^ ord($b[$i]);
}
return $result == 0;
}
答案 0 :(得分:36)
看起来他们正试图阻止timing attacks。
在加密技术中,定时攻击是一种侧通道攻击,攻击者通过分析执行加密算法所花费的时间来试图破坏密码系统。计算机中的每个逻辑操作都需要时间来执行,并且时间可以根据输入而不同;通过精确测量每个操作的时间,攻击者可以向后工作。
基本上,如果比较正确的密码和不正确的密码需要不同的时间,那么您可以使用时间来确定您正确猜到的密码的字符数。
考虑一个非常有缺陷的字符串比较(这基本上是普通的字符串相等函数,添加了明显的wait
):
function compare(a, b) {
if(len(a) !== len(b)) {
return false;
}
for(i = 0; i < len(a); ++i) {
if(a[i] !== b[i]) {
return false;
}
wait(10); // wait 10 ms
}
return true;
}
假设您提供密码,并且(一致地)一个密码需要一些时间,而另一个密码需要大约10毫秒。这告诉你什么?这意味着第二个密码比第一个密码还要多一个字符。
这可以让你进行电影黑客攻击 - 你可以一次猜出一个密码(这比猜测每一个可能的密码容易得多)。
在现实世界中,还有其他因素,所以你必须多次尝试使用密码才能处理现实世界的随机性,但是你仍然可以尝试每一个字符密码,直到很明显花费更长时间,然后开始使用两个字符密码,依此类推。
这个功能在这里仍有一个小问题:
if(strlen($a) !== strlen($b)) {
return false;
}
它允许您使用计时攻击来确定密码的正确长度,这使您无需猜测任何更短或更长的密码。一般情况下,you want to hash your passwords首先(会产生等长的字符串),所以我猜他们并不认为这是一个问题。