Почему важен порядок аргументов в PHP-функции hash_equals()?

В PHP 5.6 появилась функция hash_equals() для безопасного сравнения хэшей паролей и предотвращение тайминг-атак. Его подпись:

bool hash_equals(string $known_string, string $user_string)

Как описано в документации, $known_string и $user_string должны иметь одинаковую длину, чтобы функция эффективно предотвращала атаки по времени (в противном случае false возвращается немедленно, утечка длины известной строки).

Далее в документах говорится:

Важно указать введенную пользователем строку в качестве второго параметра, а не первого.

Мне кажется неинтуитивным, что функция несимметрична по своим аргументам.

Вопрос:

  • Почему важно, чтобы строка пользователя предоставлялась последней?

Вот выдержка из исходного кода функции:

PHP_FUNCTION(hash_equals)
{
    /* ... */

    if (Z_STRLEN_P(known_zval) != Z_STRLEN_P(user_zval)) {
        RETURN_FALSE;
    }

    /* ... */

    /* This is security sensitive code. Do not optimize this for speed. */
    for (j = 0; j < Z_STRLEN_P(known_zval); j++) {
        result |= known_str[j] ^ user_str[j];
    }

    RETURN_BOOL(0 == result);
}

Как по мне, реализация полностью симметрична относительно двух аргументов. Единственная операция, которая может что-то изменить, — это оператор XOR.

  • Возможно ли, что оператор XOR выполняется за непостоянное время в зависимости от значений аргументов? Может ли время его выполнения зависеть от порядка аргументов (например, если 1-й аргумент равен нулю)?

  • Или это примечание из документации PHP является «оговоркой» для изменений реализации в будущих версиях?


Редактировать

Как указано в Morpfh, первоначальная реализация предложения была другой:

PHP_FUNCTION(hash_compare)
{
    /* ... */

    /**
     * If known_string has a length of 0 we set the length to 1,
     * this will cause us to compare all bytes of userString with the null byte which fails
     */
    mod_len = MAX(known_len, 1);

    /* This is security sensitive code. Do not optimize this for speed. */
    result = known_len - user_len;
    for (j = 0; j < user_len; j++) {
        result |= known_str[j % mod_len] ^ user_str[j];
    }

    RETURN_BOOL(0 == result);
}

Как вы видите, черновик реализации пытался обрабатывать хэши разной длины и обрабатывал аргументы асимметрично. Возможно, эта черновая реализация не первая.

Подводя итоги: примечание в документации о порядке аргументов кажется пережитком черновой реализации.


person Alex Shesterov    schedule 12.01.2015    source источник
comment
Я всегда ненавижу, когда документация говорит вам что-то подобное и не объясняет, почему.   -  person developerwjk    schedule 13.01.2015
comment
1) это вопрос PHP, поэтому почему он помечен 'c' 2) параметры должны быть в определенном порядке, потому что именно так функция ожидает параметры.   -  person user3629249    schedule 13.01.2015
comment
@ user3629249, 1) он помечен как C, потому что PHP реализован на C, и это больше касается реализации PHP, чем использования. 2) извините, но я не могу уловить никакой информации в вашем последнем утверждении.   -  person Alex Shesterov    schedule 13.01.2015
comment
Контракт следует принимать по стоимости. Почитай это.   -  person user2864740    schedule 14.01.2015
comment
@ user2864740: +1, да! Этот вопрос имел не практическое значение, а чистое любопытство.   -  person Alex Shesterov    schedule 14.01.2015


Ответы (1)


Обновлять:

См. комментарий Рувена Веслинга (под этим ответом).


Это больше предположение, чем ответ, но, возможно, вы что-то из этого почерпнете.


Одно из предположений, как вы упомянули, заключается в том, что для вероятной обратной совместимости функция претерпит изменения в будущем по какой-либо причине, чтобы (1) не возвращать false на равной длине; таким образом, будучи уязвимым для информации о длине утечки - или (2) других алгоритмов/проверок, где нужно знать, что есть что - или (n) ...


Вероятным кандидатом является то, что это остаток от предложения к реализации:

Таким образом, из Proposal можно получить:

Пользователи должны быть внимательны, так как важно, чтобы введенная пользователем строка (или хэш этой строки) использовалась в качестве второго параметра, а не первого.

Это присутствует с момента создания предложения:

Которые могут быть связаны с References, например:

где не возвращаются на одинаковую длину, а зацикливаются на useLen.

person Morpfh    schedule 13.01.2015
comment
Вероятно, вы правы в том, что это пережиток более ранней реализации (или ее документации). Я не догадался заглянуть в историю. Реализация предложения (вы предоставили ссылку) сравнивала строки разной длины по байтам и была асимметричной при обработке аргументов. - person Alex Shesterov; 14.01.2015
comment
Автор RFC и патча здесь. Вы совершенно правы. Моя первоначальная идея состояла в том, чтобы попытаться избежать утечки длины. Я оставил имена параметров для прямой совместимости (см. marc.info/?l=php -internals&m=139318035405396&w=2) Также рассмотрите этот вопрос на сайте security.stackexchange.com, который был задан в рамках обсуждения реализации PHP. - person Rouven Weßling; 14.01.2015