Почему порядок аргументов в функции 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 выполняется в непостоянное время, в зависимости от значений аргументов? Может ли его время выполнения зависеть от порядка аргументов (например, если первый аргумент равен нулю)?

  • Или это примечание от 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);
}

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

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

Ответ 1

Обновление:

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

...


Это скорее размышление, чем ответ, но, возможно, вы получите что-то из этого.


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


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

Таким образом, из предложения одно:

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

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

Что может быть связано с ссылками, например:

где один не возвращается на равной длине, но loop useLen.