Уязвимость приложения из-за неслучайных хеш-функций

Ниже приведена выдержка из статьи, в которой объясняется возможность атаки типа "отказ в обслуживании" (DoS) из-за случайные хэш-функции, используемые в структурах хэш-данных.

[…] это условие можно использовать, используя предсказуемые коллизии в базовых алгоритмах хеширования.

Чтобы проверить это, я просмотрел эталонную реализацию Java HashMap от Oracle и действительно нашел используемые статические хэш-функции:

    static int hash(int h) {
       h ^= (h >>> 20) ^ (h >>> 12);
       return h ^ (h >>> 7) ^ (h >>> 4);
    }

В другом статье по этой теме говорится:

Сервер Tomcat 6.0.32 анализирует строку конфликтующих ключей размером 2 МБ примерно за 44 минуты процессорного времени i7, поэтому злоумышленник со скоростью около 6 кбит/с может постоянно загружать одно ядро ​​i7. Если у злоумышленника есть гигабитное соединение, он может занять около 100 000 ядер i7.

Как мы можем защититься от этой уязвимости. Более того, с таким количеством программного обеспечения, которое мы используем с открытым исходным кодом (Tomcat и т. Д.), Которые полагаются на эту реализацию.


person Sumit    schedule 29.12.2011    source источник
comment
Я читал кое-что, где этот кот выпустил исправление для этой проблемы. Быстрый и безопасный способ — исправить обновление.   -  person kosa    schedule 29.12.2011
comment
Чтобы быть уверенным, я бы сам исправил хэш HashMap или String hashCode, если у tomcat нет исправления, которое вы можете использовать.   -  person Peter Lawrey    schedule 29.12.2011
comment
@thinksteep Я взял tomcat в качестве примера. Может быть множество других приложений в зависимости от эталонной реализации, если они не разыгрывают свою собственную реализацию структур хеш-данных, например, как предложил Питер. Мне интересно, почему RI напрямую не предоставляет исправление для этого.   -  person Sumit    schedule 29.12.2011
comment
Предложите миграцию на Serverfault или Security.SE.   -  person Ladadadada    schedule 29.12.2011
comment
Хотя это, конечно, применимо к Java (и ряду других языков) в целом, стоит отметить, что Tomcat уже имеет исправления в стволе для ветвей 6 и 7. См. markmail.org/message/.   -  person Cheekysoft    schedule 04.01.2012
comment
Стоит знать, что в Java SE 7 есть альтернативный режим хеширования, доступный в JRE, который защищает от этой проблемы; хотя по умолчанию он отключен из соображений обратной совместимости. В Java 8 эта проблема была решена более полно JEP-180 (openjdk.java.net/jeps/180< /а>)   -  person Andy Lynch    schedule 07.05.2014


Ответы (5)


Понимание вектора атаки

Как работают хэш-карты

Скажем, форма комментариев в блоге принимает параметры — first_name, last_name, comment — в качестве параметров поста. Внутри Tomcat сохраняет эти параметры как HashMap.

Логическая структура этого HashMap такова:


"first_name" --> "Sripathi"
"last_name" --> "Krishnan"
"comment" ---> "DoS using poor Hashes"

Но физическая структура отличается. Ключи сначала преобразуются в хэш-код, а затем хэш-код преобразуется в индекс массива.

Таким образом, идеальная физическая структура становится:


0 --> "Sripathi"
1 --> "Krishnan"
2 --> "DoS using poor Hashes"

Но возможные ключи бесконечны. Таким образом, в какой-то момент два ключа будут иметь одинаковый хэш-код. Это становится хеш-коллизией.

При столкновениях физическая структура становится:


0 --> "Sripathi", "Krishnan"
1 --> Empty
2 --> "DoS using poor hashes"

Хэш-коллизии и влияние на производительность

Когда у вас возникают коллизии хэшей, вставка новой записи означает перебор всех элементов в одном хэш-«сегменте» последовательно только для того, чтобы выяснить, существует ли он уже на карте. Вставка одного элемента может приблизиться к сложности O(n), если все элементы хэшируют одно и то же значение. Вставка n элементов в этом наихудшем случае делает его сложность O(n*n).

Вкратце: если вы вставите тысячи ключей с одинаковым хэш-кодом, серверу потребуется много циклов ЦП.

Как вы генерируете ключи с одним и тем же хэшем?

В Java «Aa» и «BB» имеют одинаковый хеш-код.

Благодаря свойству «Эквивалентные подстроки» мы можем сгенерировать несколько других строк с тем же хэш-кодом, просто начав с этих двух строк.

Первая итерация: «AAAA», «AABb», «BbAA», «BbBb» имеют одинаковый хеш-код.

Теперь у нас есть 4 строки с одинаковым хеш-кодом. Мы можем переставить их, чтобы сгенерировать 16 строк с одинаковым хэш-кодом. Например :


"AaAaAaAa", "AaAaBBBB", "AaAaAaBB", "AaAaBBAa",
"BBBBAaAa", "BBBBBBBB", "BBBBAaBB", "BBBBBBAa",
"AaBBAaAa", "AaBBBBBB", "AaBBAaBB", "AaBBBBAa",
"BBAaAaAa", "BBAaBBBB", "BBAaAaBB", "BBAaBBAa",

Все эти 16 строк имеют одинаковый хеш-код.

Теперь вы можете взять эти 16 строк и сгенерировать 256 строк с одинаковым хэш-кодом.

Вкратце: очень легко сгенерировать большой набор строк, которые будут иметь точный хэш-код.

Как вы атакуете сервер?

  1. Создать тысячи строк с одинаковым хеш-кодом (см. выше)
  2. Создайте запрос POST следующим образом: AaAa=&AaBB=&BBAa=&BBBB= ....
  3. Отправить форму
  4. Повторите в цикле и создайте несколько потоков, чтобы использовать все ресурсы сервера.

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

Профилактика

Как правило, базовая платформа не может это исправить. Это считается проблемой структуры приложения. Другими словами, это должен исправить Tomcat, а не Oracle/Sun.

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

  1. Ограничение количества параметров POST. В Tomcat 6.0.35+ появился новый параметр maxParameterCount. Значение по умолчанию — 10 000. Чем ниже, тем лучше, если это не нарушает вашу функциональность.

  2. Ограничьте размер POST-запроса. Чтобы атака сработала, полезная нагрузка должна быть огромной. Размер POST по умолчанию, разрешенный Tomcat, составляет 2 МБ. Уменьшение этого значения до 200 КБ снизит эффективность этой атаки. Параметр в tomcat — maxPostSize.

  3. Брандмауэр веб-приложения. Если у вас есть брандмауэр веб-приложения, вы можете настроить его так, чтобы блокировать запросы, которые выглядят подозрительно. Это реактивная мера, но она полезна, если вы не можете использовать одно из вышеперечисленных решений.

К сведению: документация Tomcat находится здесь — http://tomcat.apache.org/tomcat-6.0-doc/config/http.html

person Sripathi Krishnan    schedule 29.12.2011
comment
Извините - это должны быть Aa и BB, оба имеют хэш-код 2112. Сообщение обновлено соответственно. - person Sripathi Krishnan; 02.01.2012
comment
Java HashMap не выполняет цепочку при возникновении коллизии, если для ключа «k» уже сохранено значение, оно заменит это значение новым значением. В соответствии с java documentaiton связывает указанное значение с указанным ключом в этой карте. Если карта ранее содержала сопоставление для этого ключа, старое значение заменяется. пожалуйста, проверьте это docs.oracle.com/javase/1.4.2/docs/api/java/util/, java.lang.Object). Поэтому я не полностью согласен с вашими коллизиями хэшей и их влиянием на производительность. ваше объяснение хорошо подходит для хеширования в целом - person Rajesh Pantula; 02.01.2012
comment
@ rao_555: я говорю о двух разных ключах с одинаковым хеш-значением. Рассмотрим хэш-карту с ключами Aa и BB. В этом случае значение не будет заменено, а производительность ухудшится, поскольку Hashmap по сути станет Linkedlist. - person Sripathi Krishnan; 02.01.2012
comment
Также исправьте строку First Iteration. AAAA, AABb, BbAA, BbBb неправильно. Остальное правильно. Однако пример был бы понятнее с Ea и FB, поскольку дело в том, что a и B отличаются на 31, а E и F отличаются на 1. - person cdunn2001; 07.01.2012
comment
@Sripathi Krishnan На моей машине хэш-код для Aa и BB равен 2260. Я использовал код int hash = hash(Aa.hashcode()); - person easycoder; 08.01.2012
comment
JDK может и должен исправить это. Это можно сделать с помощью криптографической хеш-функции, такой как SipHash. - person Demi; 16.03.2017
comment
@RajeshPantula Вы путаете хэш-ключи и значения хэш-ключа (хеш-коды). Конфликт хэш-ключа, конечно, заменяет значение ключа. Но коллизия хэш-кода определенно выполняет цепочку. Просто попробуйте добавить Aa и BB в хэш-карту и убедитесь, что (а) строковые ключи имеют одинаковое значение hashCode и (б) оба элемента остаются разными ключами на карте. - person Christopher Schultz; 08.04.2019

Самое простое решение — перейти на фиксированную версию tomcat. Тем не менее, я подозреваю, что вы хотите знать подробности о том, что людям с котами нужно изменить.

Эта атака работает, используя общую деталь реализации структур хеш-данных — использование связанных списков для хранения всех значений, чей хеш-код одинаков. Добавление значений в этот связанный список неэффективно, так как размер списка становится большим. Злоумышленник может создать список значений, которые, как известно, генерируют конфликтующие хэши, вызывая такое неэффективное поведение. Чтобы защититься от этого, у вас есть несколько вариантов:

  • Предотвратить коллизии — не дать злоумышленнику генерировать конфликтующие значения, используя некоторый (псевдо) случайный фактор в вашей хэш-функции. Perl сделал это уже давно.

  • Используйте для корзин что-то помимо связанных списков — атака работает, потому что вставка N элементов в связанный список приводит к росту N^2. Если вы используете сбалансированное дерево или какую-либо другую структуру, которая имеет рост N logN при вставке, проблема должна быть смягчена. Это может пожертвовать некоторой лучшей/средней производительностью случая, чтобы ограничить, насколько плох худший случай.

person Peter Recore    schedule 29.12.2011
comment
Спасибо, Питер. Мой вопрос был больше с точки зрения хеш-структур Java по умолчанию. Они используют статическую хеш-функцию. Думаю, мне нужно переопределить часть хеширования, чтобы защититься от этой уязвимости. - person Sumit; 29.12.2011
comment
Тот факт, что Java не рандомизирует свои хэши, не является проблемой. Проблема заключается в том, что сервлет-контейнеры не сохраняют параметры сообщений в картах, поскольку это позволяет легко провести эффективную DoS-атаку, не требуя большой пропускной способности. Вам не нужно ничего менять в вашей Java-программе, если только вы не реализуете веб-сервер, который хранит параметры POST в HashMap. - person JB Nizet; 29.12.2011
comment
@JBNizet Я согласен в этом конкретном случае. Его легче всего использовать. Но скажем, предыдущий разработчик с общедоступным веб-сайтом, который знает внутренности системы, всегда может взломать слабые методы хеширования. По крайней мере, он может замедлить работу системы, потому что знает, какая функция используется. Прошу прощения за то, что предположил злых разработчиков :) - person Sumit; 29.12.2011
comment
@JB Nizet - Если вы действительно параноик, вам нужно быть осторожным с любой хеш-структурой, в которой хранятся данные, контролируемые пользователем, а не только с параметрами POST. Кажется, что всегда использовать безопасный алгоритм хэширования не так уж и плохо. Если производительность очень важна, вы можете явно использовать небезопасный алгоритм. Или мы могли бы использовать хэш-структуру, которая переключается на безопасный алгоритм, когда мы замечаем вставку эксплуатирующих ключей. - person Peter Recore; 29.12.2011
comment
Проблема не строго в том, что используется разрешение связанного списка. Коллизии хэшей будут влиять на другие реализации хеш-таблиц, например те, которые не используют отдельная цепочка, так же легко. Проблема в том, что генерируется один и тот же хэш, что лишает смысла распределять ключи по доступным корзинам — любая открытая адресация, использующая только начальное значение хэша, будет иметь ту же проблему вырождения. (Без использования дополнительных/внутренних хэш-функций я не знаю, как реализация хеширования O(1) могла бы избежать этой атаки.) - person user2864740; 17.03.2014

Затронутая версия Tomcat: Apache Tomcat ‹= 5.5.34, ‹= 6.0.34, ‹= 7.0.22 согласно предоставленной вами ссылке. На странице перечислены Apache Tomcat >= 5.5.35, >= 6.0.35, >= 7.0.23 как фиксированные версии.

person Peter Lawrey    schedule 29.12.2011

Вот некоторый код Python для генерации этих ключей... Еще не тестировал его, но было бы интересно получить отзывы о нем:

#!/usr/bin/python
import sys
alphabet = ["Aa","BB"]

def func(str, length):
                global alphabet
                if(length != 0):
                                for x in alphabet:
                                                new_str = str+x
                                                func(new_str, length-1)
                else:
                                sys.stdout.write(str+"=&")


for x in range(1,16):
        func("",x)
person ossys    schedule 29.05.2012

Java HashMap/HashTable может выполнять операцию «изменения размера», когда заполненная запись достигает порогового значения. Трудно сказать, что вас ждет HashMap с фиксированным ведром. Из-за того, что операция выбора корзины состоит из двух шагов: первый — получение хеш-значения указанного ключа; другим основным шагом является операция остатка с общим размером корзины (размер изменяется путем «изменения размера»).

person Denny Ye    schedule 06.01.2012