<?php
$find = "hello";
$length = array_combine(range(1, 10), array_fill(1, 10, 0));
for ($i = 0; $i < 1000000; $i++) {
for ($j = 1; $j <= 10; $j++) {
$testValue = str_repeat('a', $j);
$start = microtime(true);
if ($find === $testValue) {
//do Nothing
}
$end = microtime(true);
$length[$j] += $end - $start;
}
}
arsort($length);
$length = key($length);
var_dump($length . " found");
$found = '';
$alphabet = array_combine(range('a', 'z'), array_fill(1, 26, 0));
for ($len = 0; $len < $length; $len++) {
$currentIteration = $alphabet;
$filler = str_repeat('a', $length - $len - 1);
for ($i = 0; $i < 1000000; $i++) {
foreach ($currentIteration as $letter => $time) {
$testValue = $found . $letter . $filler;
$start = microtime(true);
if ($find === $testValue) {
//do Nothing
}
$end = microtime(true);
$currentIteration[$letter] += $end - $start;
}
}
arsort($currentIteration);
$found .= key($currentIteration);
}
var_dump($found);
这是在搜索具有以下约束的单词
仅限a-z 最多10个字符
脚本找到单词的长度没有问题,但是单词的值永远不会像定时攻击那样回复。
我做错了吗?
脚本循环遍历长度,正确识别长度。它然后循环每个字母(a-z)并检查它们的速度,理论上&#39; haaaa&#39;应该比'aaaaa&#39;由于第一个字母是h,然后它继续为5个字母中的每个字母。
跑步会带来像&#39; brhas&#39;这显然是错的(每次都不一样,但总是错的)
答案 0 :(得分:3)
我做错了吗?
我不这么认为。我尝试了你的代码,我和你以及在评论中尝试的其他人一样,获得了第二个循环的完全随机的结果。第一个(长度)大部分是可靠的,但不是100%的时间。顺便说一下,建议的$argv[1]
技巧并没有真正提高结果的一致性,说实话,我真的不明白它为什么要这样做。
由于我很好奇,我看了一下PHP 7.1的源代码。字符串标识函数(zend_is_identical
)如下所示:
case IS_STRING:
return (Z_STR_P(op1) == Z_STR_P(op2) ||
(Z_STRLEN_P(op1) == Z_STRLEN_P(op2) &&
memcmp(Z_STRVAL_P(op1), Z_STRVAL_P(op2), Z_STRLEN_P(op1)) == 0));
现在很容易理解为什么对长度的第一次定时攻击效果很好。如果长度不同,则永远不会调用memcmp
,因此返回的速度要快得多。即使没有太多的迭代,这种差异也很容易引起注意。
一旦你知道了长度,在你的第二个循环中,你基本上是试图攻击潜在的memcmp
。问题是时间差异很大程度上取决于:
memcmp
我推荐这篇标题为"Benchmarking memcmp for timing attacks"的文章,以获得更详细的解释。他们做了一个更精确的基准测试,仍然无法在时间上获得明显的明显差异。我只想引用文章的结论:
总之,如果
memcmp()
受到时间攻击,它在很大程度上取决于具体情况。