我一直在努力寻找有关password_verify()
是否使用长度 - 常数时间比较以避免时间攻击的信息。
现在,简单的例子:
$hash = '$2y$10$HH3906lfby7HOy1N3duQh.Kju.84ct6AcMZm2p/SYZsZSXuYWvvT.';
$startTime = microtime(TRUE);
password_verify('rasmuslerdorf', $hash);
$endTime = microtime(TRUE);
$time = $endTime - $startTime;
这总是产生稍微不同的输出,根据this article(“为什么此页面上的散列码比较”长度常数“时间中的散列?”段落) ,可以用于定时攻击来获取哈希值。我认为这些结果看起来有点随机,但它们肯定不会保持不变。
问题是,password_verify()
是否使用长度 - 常数时间比较来避免时间攻击?在文档中没有相关信息,由于我的经验不足,我无法很好地解释函数处理时间结果。
答案 0 :(得分:8)
答案是肯定它使用长度 - 恒定时间比较。
这是php的password_verify函数的摘录
/* We're using this method instead of == in order to provide
* resistance towards timing attacks. This is a constant time
* equality check that will always check every byte of both
* values. */
for (i = 0; i < hash_len; i++) {
status |= (ret->val[i] ^ hash[i]);
}
您可以查看https://github.com/php/php-src/blob/master/ext/standard/password.c
处的完整源代码答案 1 :(得分:4)
简短回答:是的,确实如此。
答案很长:没有必要。
要理解为什么没有必要,我们需要查看被比较的字符串:
$2y$10$9JxHB8U1QKsLS/ynplKzm.iIO7f6gtTKYA61ppVuANYxWNCA5DW1S
$2y$10$ILlWQrYyDJvHHkxcCgjm7OThLRAmMcTzsJOZOwjaSYiRUHq8LVYde
$2y$10$8JfydDKUNbOeiybwZ9m.j.5TC8CBqkc3RZu2DX42A4dFNpNYPWfzm
$2y$10$qeG.53lr9PVVGN4Yk.kSZuOMpfone5kINyWVpAf2gUXPseU2WdSzK
$2y$10$nZUgPUwiXIvCJ9BY1wbtbuV5vH6yff9CNyumFsI/NN2eJmf20iec.
这是同一密码的5个不同哈希值。格式为:
$2y$10$saltsaltsaltsaltsaltsahashhashhashhashhashhashhashhas
现在,对于一个远程攻击者(一个将进行定时攻击的人)来说,盐是一个秘密。当我们重新散列他们的尝试时,盐是相同的。例如:
stored password "test":
hash = $2y$10$9JxHB8U1QKsLS/ynplKzm.iIO7f6gtTKYA61ppVuANYxWNCA5DW1S
如果攻击者尝试密码&#34; abc&#34;,则内部password_verify()
会调用crypt("abc", hash)
。这将导致:
$2y$10$9JxHB8U1QKsLS/ynplKzm.FTYpGS/gNDw4SB6YD0wEtCSPgGvtPim
现在,让我们并排看看这两个哈希:
$2y$10$9JxHB8U1QKsLS/ynplKzm.iIO7f6gtTKYA61ppVuANYxWNCA5DW1S
$2y$10$9JxHB8U1QKsLS/ynplKzm.FTYpGS/gNDw4SB6YD0wEtCSPgGvtPim
注意盐是一样的吗?请注意,直到第一个.
之前的所有内容都是相同的。还要注意攻击者不知道盐是什么。
如果攻击者能够对比分进行计时攻击,那就没有用了。因为他们不知道盐(并因此推断出哈希只是浪费时间,因为没有盐,他们无法确定密码)。
因此,时间安全并非严格必要。
为什么包括在内?因为每个人都会犯错误。因为纵深防御是一个好主意。因为这个分析假定在没有盐的情况下没有关于哈希的任何内容(例如:如果bcrypt中的缺陷基于密码偏向哈希,那么如果不知道盐,密钥空间将从72 ^ 255减少)。
简而言之,这是件好事,但并非绝对必要......