
Поднявшаяся в этом топике дискуссия о криптостойкости многократного применения хеша над паролем (проскальзывавшая, кстати, и в других форумах), подтолкнула меня к этому немного математическому топику. Суть проблемы возникает из идеи многократной (1.000 и более раз) обработки пароля перед хранением каким-либо криптостойким алгоритмом (чаще всего хеш-функцией) с целью получить медленный алгоритм проверки, тем самым эффективно противостоящий brute force-у в случае перехвата или кражи злоумышленником этого значения. Как совершенно верно отметили хабрапользователи Scratch (автор первой статьи), mrThe и IlyaPodkopaev, идея не нова и ею пользуются разработчики оборудования Cisco, архиватора RAR и многие другие. Но, поскольку хеширование – операция сжимающая множество значений, возникает вполне закономерный вопрос – а не навредим ли мы стойкости системы? Попытка дать ответ на этот вопрос –
Снижение стойкости будем рассматривать в двух моделях угроз:
- перебор злоумышленником случайных входных наборов байт в надежде найти коллизию из-за сужения пространства значений («полный перебор»);
- перебор по словарю.
Начнем с первого, более теоретического, вопроса: «сможет ли такое огромное пространство значений современной хеш-функции 2128 (MD5), 2160 (SHA-1), 2256 (ГОСТ Р 34.11, SHA-256) в результате многократного применения сузиться до такой степени, что коллизию к нему можно будет подобрать простым случайным поиском ?».
Начало
Одним из основных свойств/требований к криптостойким хеш-функциям является очень хорошая равномерность распределения выходных результатов. Это позволяет оперировать в потребующихся далее оценках обычной теорией вероятности и комбинаторикой.
Возьмем входное множество из N элементов и каждому из его объектов сопоставим в выходном множестве также из N объектов случайным образом какой-либо элемент (см. рис.). Очевидно, что несколько отображений покажут на одинаковые элементы, а, следовательно, останутся и элементы, к которым не подходит ни одной стрелки:

Вероятность того, что конкретный элемент выходного множества НЕ будет выбран конкретным отображением (стрелкой) равна



Предел


Детально
Как только входное множество начинает быть заполненным не полностью (т.е. после второй итерации в первой модели угроз или сразу же во второй модели угроз (с атакой по словарю)), начинают работать другие формулы и другие закономерности. Это хорошо видно на том же рисунке для случая, если во входном множестве было всего 3 «активных» элемента. Вероятность «потерять» элемент (сузить выходное множество) резко уменьшается, т.к. теперь гораздо реже два и более отображения встречаются на одном и том же элементе.

Пусть во входном множестве было активно только kN элементов, где 0<k<1. Тогда вероятность НЕ выбора конкретного элемента равна



Номер итерации, i | Доля множества значений, k |
---|---|
1 | 1,0000 |
2 | 0,6321 |
3 | 0,4685 |
4 | 0,3740 |
5 | 0,3120 |
6 | 0,2680 |
Конечно, доля активных элементов продолжает с каждой итерацией уменьшаться, но не так быстро и катастрофично как в самом начале.
Для того чтобы оценить степень снижения стойкости для большого количества итераций удобнее изучать не само число k, а обратное ему – во сколько раз сократилось выходное множество, причем сразу в логарифмической шкале по основанию 2, чтобы единицами измерения стали биты:


Точки, в которых график пересекает целые значения r, сведены в таблицу:
Снижение стойкости, r (бит) | Кол-во итераций, i |
---|---|
4 | 30 |
5 | 61 |
6 | 125 |
7 | 253 |
8 | 509 |
9 | 1.020 |
10 | 2.044 |
11 | 4.092 |
12 | 8.188 |
13 | 16.379 |
14 | 32.763 |
15 | 65.531 |
16 | 131.067 |
... | ... |
20 | 2.097.146 |
... | ... |
24 | 33.554.425 |
Как видим, снижение стойкости с определенного момента нарастает очень медленно. Более того, количество итераций в точности следует степеням двойки:

Проверка адекватности модели
Как всегда после большого количества теоретических измышлений, возникает желание хоть как то оценить, насколько далека полученная теория от практики. В качестве подопытной хеш-функции мною были взяты первые 16 бит от MD5 (криптостойкая хеш-функция позволяет выбирать любой набор бит результата для формирования усеченной хеш-функции с аналогичными свойствами, хотя и, естественно, меньшей стойкости – соответственно своей новой разрядности). На вход алгоритма подаются все возможные значения от 0 до 0xFFFF, над каждым из значений проводится i итераций, каждая итерация заключается в вычислении первых 16 бит MD5-результата (получается 16-разрядная хеш-функция). «Результаты опытов» приведены в таблице:
Планируемое снижение стойкости, r (бит) | Кол-во итераций, i | Ожидаемое кол-во элементов в множестве | Наблюдаемое кол-во элементов в множестве |
---|---|---|---|
4 | 30 | 216-4=4.096 | 3.819 |
6 | 125 | 216-6=1.024 | 831 |
8 | 509 | 216-8=256 | 244 |
Конечно, при таком малом значении N и особенно kN начинает сразу сказываться тот факт, что все выкладки выше приведены для пределов (для N сравнимых и больших 2128 – там всё хорошо). Но даже и здесь общая тенденция прослеживается и приемлемо укладывается в полученные выше формулы.
Следствие – стойкость к словарной атаке
Пришло время проанализировать, «ощутимо ли сужение области значений в тех случаях, когда в качестве парольной фразы выбран словарный или легко конструируемый пароль?». Очевидно, что в этом случае «активное» входное множество значений уже чрезвычайно сужено – оно находится в зависимости от методик подбора в диапазоне от 216 до 248. Таким образом, мы сразу попадаем в «разряженную» ситуацию – в правую часть графика. Здесь вероятность отображения двух разных входных элементов на один и тот же выходной чрезвычайно мала. Миллионы и миллионы операций практически нисколько не изменят количество элементов в выходном множестве. Отсеются единицы/десятки – ни о каком практическом снижении стойкости речи не идет.
Выводы
Описанную схему защиты от brute force-а можно свободно применять на практике практически без ограничений со стороны криптографической стойкости. Рекомендуемые верхние пределы количества итераций (разрешенное снижение стойкости r выбрано субъективно в размере 1/8 разрядности хеш-функции) приведены в таблице:
Разрядность хеш-функции (бит) | Разрешенное снижение стойкости, r (бит) | Верхний предел разрешенного количества итераций, i |
---|---|---|
128 | 16 | 130.000 |
160 | 20 | 2.000.000 |
192 | 24 | 32.000.000 |
224 | 28 | 500.000.000 |
256 | 32 | 8.000.000.000 |