Hash Collision - каковы шансы?

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

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


person alex    schedule 18.11.2008    source источник
comment
Задайте вопрос, сколько вам будет стоить столкновение. Если это бесплатный сайт, отлично. Если вы ведете бизнес, приносящий прибыль, и перезапись обойдется вам в контракт в миллион долларов, я бы подумал еще раз.   -  person Martin York    schedule 18.11.2008
comment
Если вам нужно скрыть некоторые данные в своем URL-адресе, чтобы скрыть данные, вы делаете что-то не так.   -  person Arkh    schedule 18.11.2009
comment
Почему? представьте себе сценарий, в котором вы продаете цифровые товары, и к этим товарам можно получить доступ через API. некоторые по цене, а некоторые нет. Это лучший способ ссылаться на них через URL-адрес без изменения пользователем URL-адресов и загрузки других неавторизованных приложений.   -  person Faisal Abid    schedule 20.11.2009
comment
Или вы можете реализовать уровни доступа и проверять, есть ли у людей доступ к вашим данным, прежде чем отправлять их им вслепую. Да, вам нужно поработать, чтобы сделать это, но вам платят за это, а не за реализацию безопасности посредством неизвестности, которая и так уже потерпела неудачу. Никогда не доверяйте данным, исходящим от пользователя.   -  person Arkh    schedule 20.11.2009
comment
Я склонен с этим согласиться. Хотя бывают случаи, когда хеширование данных и забота об их уникальности важны (на ум приходят Mercurial ID), если вам нужно скрыть свои идентификаторы по соображениям безопасности, это очень опасная модель безопасности. А если в этом нет необходимости, зачем?   -  person dimo414    schedule 21.06.2010
comment
Есть один очевидный пример противодействия этому: URL-адреса для сброса пароля. Обычно считается безопасным, когда элементы работают вместе. Что-то дано пользователю - URL сброса; что-то, что они знают или имеют - контроль над своим адресом электронной почты и / или ответом на секретный вопрос; что-то, что они должны сделать - ответить на электронное письмо о сбросе до истечения срока его действия.   -  person Patrick M    schedule 06.08.2012


Ответы (11)


Если вы предполагаете, что SHA-1 работает хорошо, вы можете сделать вывод, что вероятность того, что два заданных сообщения имеют одинаковый хэш, составляет 1 из 2 ^ 160 (поскольку SHA-1 создает 160-битный хеш).

2 ^ 160 - смехотворно большое число. Это примерно 10 ^ 48. Даже если в вашей базе данных миллион записей, вероятность того, что новая запись будет иметь тот же хэш, составляет 1 из 10 ^ 42.

SHA-1 оказался довольно хорошим, поэтому я не думаю, что вам вообще нужно беспокоиться о коллизиях.

В качестве побочного примечания используйте функцию PHP raw_output при использовании SHA-1, поскольку это приведет к более короткой строке и, следовательно, сделает ваши операции с базой данных немного быстрее.

РЕДАКТИРОВАТЬ: Чтобы решить парадокс дня рождения, база данных с 10 ^ 18 (миллион миллионов миллионов) записей имеет шанс примерно 1 на 0,0000000000003 столкновения. На самом деле не о чем беспокоиться.

person Artelius    schedule 18.11.2008
comment
Всем, кто действительно верит в свободу от столкновений, помните об эффекте дня рождения. Ваше первое столкновение может произойти случайным образом с большей вероятностью, чем вы можете себе представить. Так что будьте осторожны в любом случае - person Robert Gould; 18.11.2008
comment
Да, но одно столкновение не убьет вашу систему. Ваша собственная ошибка будет. Я не думаю, что нам следует беспокоиться о том, что случается случайно один раз в десятилетие, за исключением ядерной фабрики. Если бы я только мог раздражаться ... ;-) - person e-satis; 18.11.2008
comment
Вероятность первого столкновения составляет 50% после первых 2 ^ 80 хешей. - person Seun Osewa; 24.11.2008
comment
@Seun: Нет, это совершенно неправильно. Прочтите о парадоксе дня рождения. - person Artelius; 19.11.2009
comment
ваша система выйдет из строя, если вам придется создать запись БД для каждого атома в наблюдаемой Вселенной, которая, по оценкам, составляет 10 ^ 80! Лучше использовать хэш SHA1, объединенный с идентификационным номером записи. - person Michael Butler; 13.08.2012
comment
@Artelius Означает ли 1 из 0,0000000000003 1 из 3,333 миллиарда? Или, возможно, шанс 0,0000000000003%? Пожалуйста, поправьте меня, если я ошибаюсь. - person Addison; 05.08.2016
comment
Парадокс дня рождения точно говорит, что @Artelius, я думаю, вы упустили, что это квадратный корень (sqrt (2 ^ 160) = 2 ^ 80). Обратите внимание, что sqr (365) около 20. - person MrIo; 08.12.2020

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

Это позволяет использовать разумные значения при разговоре с БД без каких-либо конфликтов, обеспечивает большую безопасность при разговоре с клиентом и снижает вероятность попадания на thedailyWTF примерно на 2 ^ 160.

См. Также Забивать гвоздь: старый башмак или стеклянная бутылка?!

person David Schmitt    schedule 18.11.2008

почему бы не сделать что-то, что гарантирует отсутствие коллизий, а также гарантирует, что никто не может изменить параметр GET для просмотра того, чего не следует: используя соль, объедините идентификатор и его хэш.

$salt = "salty";
$key = sha1($salt . $id) . "-" . $id;
// 0c9ab85f8f9670a5ef2ac76beae296f47427a60a-5

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

person nickf    schedule 18.11.2008
comment
предпочтительно использовать HMAC (hash_hmac в PHP), который, как нам сказали, устраняет некоторые недостатки с помощью такой простой схемы. en.wikipedia.org/wiki/HMAC - person araqnid; 18.11.2009

Если вы используете в качестве входных данных численно увеличивающиеся идентификаторы, то вероятность столкновения SHA-1 практически равна нулю.

Если идентификатор является единственным входом, то SHA-1 кажется излишним - создание 160-битного хэша из 32-битного целого числа. Я бы предпочел использовать модульное возведение в степень, например выберите большое (32-битное) простое число p, вычислите модульный генератор g этой группы, а затем используйте g ^ id. Это гарантирует отсутствие коллизий и дает только 32-битные «хэши».

person Martin v. Löwis    schedule 18.11.2008
comment
Идентификатор - не единственный ввод. Есть некоторые специфические данные и time () rand (), чтобы немного запутать ситуацию. - person alex; 18.11.2008
comment
Тогда простая генерация 160 случайных битов будет достаточно уникальной - нет необходимости генерировать какой-либо хеш (он не станет более уникальным с помощью хэша и не станет более случайным). - person Martin v. Löwis; 18.11.2008

SHA-1 создает дайджест длиной 160 бит. Поэтому вы в безопасности, пока у вас меньше 2 ^ (160/2) записей. Разделение на 2 связано с парадоксом дня рождения.

person Szere Dyeri    schedule 18.11.2008
comment
Безопасный, безусловно, относительный термин. Дело не в том, что до определенного момента это безопасно, а после - небезопасно. Имеет смысл говорить только о вероятностях столкновения в определенных точках. OP может нуждаться в шансе один на миллион или лучше, или ему может понадобиться один шанс на миллиард. - person Jon Skeet; 18.11.2008
comment
@Szere Dyeri Помните, случайность непредсказуема :) - person Robert Gould; 18.11.2008
comment
Джон, ты прав. Чтобы быть более точным, ожидаемое количество N-битных хэшей, которые могут быть сгенерированы до коллизии, равно 2 ^ (N / 2), где ожидание - это формальная статистика первого порядка распределения. - person Szere Dyeri; 18.11.2008

Из первых принципов:

SHA-1 создает 160-битный дайджест. Предполагая, что он использует все битовое пространство равномерно (что, по-видимому, именно для этого он был разработан), это всего лишь 2 ^ -160 шансов на каждую вставку, что вы получите столкновение.

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

Это не означает, что вы можете полностью игнорировать вероятность столкновения.

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

person Oddthinking    schedule 18.11.2008
comment
Парадокс дня рождения увеличивает вероятность столкновения до 0,00000000000000000017347234759768070944119244813919%. На самом деле не о чем беспокоиться. - person Jeff Hubbard; 18.11.2008
comment
Джефф, я допускаю, что риск столкновения можно игнорировать почти во всех случаях. Раньше я не занимался математикой. Однако вы не указываете, сколько объектов находится в коллекции, поэтому ваша оценка вероятности столкновения бессмысленна. - person Oddthinking; 18.11.2008

Задайте вопрос, сколько вам будет стоить столкновение. Если это бесплатный сайт, отлично. Если вы ведете бизнес, приносящий прибыль, и перезапись обойдется вам в контракт в миллион долларов, я бы подумал еще раз.

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

Один из способов сделать это - поместить в ссылку идентификатор и хэш идентификатора (с некоторыми дополнительными данными).

Например: (мой PHP ржавый, поэтому общий алгоритм будет :)

id   = 5;
hash = hash("My Private String " + id)
link = "http://mySite.com/resource?id=" + id + "&hash=" + hash

Затем, когда вы получите запрос, просто подтвердите, что вы можете восстановить хеш из идентификатора. Это оставляет вас уязвимым для атаки на «My Private String», но это будет довольно сложно с вычислительной точки зрения, и вы всегда можете добавить что-то еще уникальное, что напрямую не доступно пользователю (например, идентификатор сеанса).

person Martin York    schedule 18.11.2008

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

Несмотря на то, что SHA1 имеет очень большой диапазон хеш-возможностей 2 ^ 160, это все еще конечное число. Однако входные данные, которые можно передать этой функции, буквально бесконечны. При достаточно большом наборе входных данных коллизии неизбежны.

person Ketan Patil    schedule 04.10.2017

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

Вы сами сказали, что собираетесь хешировать свои последовательные идентификаторы. Было бы легко написать тестовый пример. Переберите ~ 100000000 идентификаторов и проверьте наличие коллизий. Это не займет много времени. С другой стороны, у вас может закончиться нехватка памяти на четверть пути.

person Josh    schedule 18.11.2008

Я не думаю, что sha1 () доставит вам какие-либо проблемы, слабая генерация случайных чисел - более вероятный кандидат на коллизии.

Стефан Эссер написал хорошую статью по теме.

person Waquo    schedule 18.11.2008

Каковы шансы столкновения?

точная вероятность того, что n хэшей столкнутся с S общим количеством различных возможных хэшей:

(идеальное поведение хеш-функции, парадокс дня рождения, бла-бла-бла ...)

Вы не сможете вычислить это напрямую, поскольку это огромные числа, поэтому мы используем ограничения и делаем 2 предположения:

С этими двумя предположениями вероятность столкновения может быть вычислена с помощью:

Теперь вы можете вычислить вероятность столкновения для некоторого количества n записей. Это очень и очень точно для всего, что меньше 2 ^ 70 записей для sha1 (S = 2 ^ 160), чем хуже приближение, тем больше n подхода 2 ^ 80.

Пример

Например, если вы хотите сохранить огромное количество пользователей, в частности столько же, сколько человек в мире (~ 8 миллиардов), и вы используете sha1 (S = 2 ^ 160), вероятность столкновения будет 2,5e-29 (обратите внимание, что выполняются 2 предположения). Для справки: вероятность выиграть джекпот Euromillion составляет 7e-9 приблизительно.

Любопытство: что делать с большими (большими ?!) числами?

Вычислите предел напрямую без второго предположения.

Например, первое столкновение ожидается около квадратного корня из S (в случае sha1 n = 2 ^ 80). При этом значении второе условие не выполняется, но мы можем вычислить предел напрямую с помощью:

что составляет 40% ок. вероятности столкновения.

person MrIo    schedule 06.11.2020