PHP password_verify() 和慢等于比较
PHP password_verify() and slow equals comparison
我一直在寻找有关password_verify()
是否使用长度常数时间比较来避免定时攻击的信息。
现在,简单的例子:
$hash = 'y$HH3906lfby7HOy1N3duQh.Kju.84ct6AcMZm2p/SYZsZSXuYWvvT.';
$startTime = microtime(TRUE);
password_verify('rasmuslerdorf', $hash);
$endTime = microtime(TRUE);
$time = $endTime - $startTime;
这总是会产生略有不同的输出,根据 this article("Why does the hashing code on this page compare the hashes in "length-constant" time?" 段落),可以使用在定时攻击中获取哈希。我认为这些结果看起来有点随机,但它们肯定不是恒定的。
问题是,password_verify()
是否使用长度常数时间比较来避免定时攻击?文档中没有关于它的信息,并且由于我的浅薄经验,我无法很好地解释函数处理时间结果。
答案是肯定的,它使用长度常数时间比较。
这是 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
查看完整的源代码
简短回答:是的。
长答案:没有必要。
要了解为什么没有必要,我们需要查看正在比较的字符串:
yJxHB8U1QKsLS/ynplKzm.iIO7f6gtTKYA61ppVuANYxWNCA5DW1S
y$ILlWQrYyDJvHHkxcCgjm7OThLRAmMcTzsJOZOwjaSYiRUHq8LVYde
yJfydDKUNbOeiybwZ9m.j.5TC8CBqkc3RZu2DX42A4dFNpNYPWfzm
y$qeG.53lr9PVVGN4Yk.kSZuOMpfone5kINyWVpAf2gUXPseU2WdSzK
y$nZUgPUwiXIvCJ9BY1wbtbuV5vH6yff9CNyumFsI/NN2eJmf20iec.
这是同一密码的 5 个不同哈希值。格式为:
y$saltsaltsaltsaltsaltsahashhashhashhashhashhashhashhas
现在,对于远程攻击者(运行 定时攻击者)来说,盐是秘密。当我们重新散列他们的尝试时,盐是一样的。例如:
stored password "test":
hash = yJxHB8U1QKsLS/ynplKzm.iIO7f6gtTKYA61ppVuANYxWNCA5DW1S
如果攻击者尝试密码 "abc",内部 password_verify()
将调用 crypt("abc", hash)
。这将导致:
yJxHB8U1QKsLS/ynplKzm.FTYpGS/gNDw4SB6YD0wEtCSPgGvtPim
现在,让我们并排查看这两个哈希值:
yJxHB8U1QKsLS/ynplKzm.iIO7f6gtTKYA61ppVuANYxWNCA5DW1S
yJxHB8U1QKsLS/ynplKzm.FTYpGS/gNDw4SB6YD0wEtCSPgGvtPim
注意盐是一样的吗?请注意,直到第一个 .
之前的所有内容都是相同的。还要注意攻击者不知道盐是什么。
如果攻击者能够对比较进行定时攻击,那也没什么用。因为他们不知道盐(因此推断散列是什么只是浪费时间,因为没有盐他们无法确定密码)。
所以时间安全并不是绝对必要的。
那为什么会收录呢?因为每个人都会犯错。因为纵深防御是个好主意。因为此分析假设没有盐就没有任何关于散列的有用信息(例如:如果 bcrypt 中的缺陷基于密码使散列有偏差怎么办,所以在不知道盐的情况下密钥空间从 72^255 减少)。
简而言之,拥有它是一件好事,但并不是绝对必要的...
我一直在寻找有关password_verify()
是否使用长度常数时间比较来避免定时攻击的信息。
现在,简单的例子:
$hash = 'y$HH3906lfby7HOy1N3duQh.Kju.84ct6AcMZm2p/SYZsZSXuYWvvT.';
$startTime = microtime(TRUE);
password_verify('rasmuslerdorf', $hash);
$endTime = microtime(TRUE);
$time = $endTime - $startTime;
这总是会产生略有不同的输出,根据 this article("Why does the hashing code on this page compare the hashes in "length-constant" time?" 段落),可以使用在定时攻击中获取哈希。我认为这些结果看起来有点随机,但它们肯定不是恒定的。
问题是,password_verify()
是否使用长度常数时间比较来避免定时攻击?文档中没有关于它的信息,并且由于我的浅薄经验,我无法很好地解释函数处理时间结果。
答案是肯定的,它使用长度常数时间比较。
这是 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
查看完整的源代码简短回答:是的。
长答案:没有必要。
要了解为什么没有必要,我们需要查看正在比较的字符串:
yJxHB8U1QKsLS/ynplKzm.iIO7f6gtTKYA61ppVuANYxWNCA5DW1S
y$ILlWQrYyDJvHHkxcCgjm7OThLRAmMcTzsJOZOwjaSYiRUHq8LVYde
yJfydDKUNbOeiybwZ9m.j.5TC8CBqkc3RZu2DX42A4dFNpNYPWfzm
y$qeG.53lr9PVVGN4Yk.kSZuOMpfone5kINyWVpAf2gUXPseU2WdSzK
y$nZUgPUwiXIvCJ9BY1wbtbuV5vH6yff9CNyumFsI/NN2eJmf20iec.
这是同一密码的 5 个不同哈希值。格式为:
y$saltsaltsaltsaltsaltsahashhashhashhashhashhashhashhas
现在,对于远程攻击者(运行 定时攻击者)来说,盐是秘密。当我们重新散列他们的尝试时,盐是一样的。例如:
stored password "test":
hash = yJxHB8U1QKsLS/ynplKzm.iIO7f6gtTKYA61ppVuANYxWNCA5DW1S
如果攻击者尝试密码 "abc",内部 password_verify()
将调用 crypt("abc", hash)
。这将导致:
yJxHB8U1QKsLS/ynplKzm.FTYpGS/gNDw4SB6YD0wEtCSPgGvtPim
现在,让我们并排查看这两个哈希值:
yJxHB8U1QKsLS/ynplKzm.iIO7f6gtTKYA61ppVuANYxWNCA5DW1S
yJxHB8U1QKsLS/ynplKzm.FTYpGS/gNDw4SB6YD0wEtCSPgGvtPim
注意盐是一样的吗?请注意,直到第一个 .
之前的所有内容都是相同的。还要注意攻击者不知道盐是什么。
如果攻击者能够对比较进行定时攻击,那也没什么用。因为他们不知道盐(因此推断散列是什么只是浪费时间,因为没有盐他们无法确定密码)。
所以时间安全并不是绝对必要的。
那为什么会收录呢?因为每个人都会犯错。因为纵深防御是个好主意。因为此分析假设没有盐就没有任何关于散列的有用信息(例如:如果 bcrypt 中的缺陷基于密码使散列有偏差怎么办,所以在不知道盐的情况下密钥空间从 72^255 减少)。
简而言之,拥有它是一件好事,但并不是绝对必要的...