Временная атака с помощью PHP

Я пытаюсь создать временную атаку в php Используя php 7.1 со следующим script

<?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 символов

script находит длину слова без проблем, но значение слова никогда не возвращается с ожиданием с временной атакой.

Есть ли что-то, что я делаю неправильно?

Провод script, но длины, правильная идентификация длины. он затем зацикляет каждую букву (az) и проверяет скорость на них. Теоретически "haaaa" должен быть немного медленнее, чем "aaaaa" из-за того, что первая буква ah, затем она продолжается для каждой из 5 букв.

Запуск дает что-то вроде "brhas", которое явно неверно (каждый раз каждый раз, но всегда неправильно)

Ответ 1

Есть ли что-то, что я делаю неправильно?

Я так не думаю. Я пробовал свой код, и я тоже, как вы и другие люди, которые пытались в комментариях, получили совершенно случайные результаты для второго цикла. Первый (длина) в основном надежный, хотя и не в 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 для временных атак" для более подробных объяснений. Они сделали гораздо более точный бенчмарк и до сих пор не смогли получить явной заметной разницы в сроках. Я просто приведу цитату из статьи:

В заключение, это сильно зависит от обстоятельств, если a memcmp() подвергается временной атаке.