Хеш-функція в Python 3.3 повертає різні результати між сесіями


99

Я реалізував BloomFilter в python 3.3 і отримував різні результати кожного сеансу. Знизивши цю дивну поведінку, я прийшов до внутрішньої функції hash () - вона повертає різні значення хешу для одного і того ж рядка кожного сеансу.

Приклад:

>>> hash("235")
-310569535015251310

----- відкриття нової консолі python -----

>>> hash("235")
-1900164331622581997

Чому це відбувається? Чому це корисно?

Відповіді:


136

Python використовує випадкове насіння хешу, щоб запобігти зловмисникам перебирати ваш додаток, надсилаючи вам ключі, призначені для зіткнення. Див. Оригінальне розкриття інформації про вразливість . Завдяки компенсації хешу випадковим насінням (встановленим один раз під час запуску) зловмисники більше не можуть передбачити, які ключі зіткнуться.

Ви можете встановити фіксоване насіння або вимкнути функцію, встановивши PYTHONHASHSEEDзмінну середовища ; типовим значенням є, randomале ви можете встановити для нього фіксоване ціле додатне значення, а 0функцію взагалі вимкнути.

У версіях Python 2.7 та 3.2 функція за замовчуванням вимкнена (використовуйте -Rперемикач або встановіть, PYTHONHASHSEED=randomщоб увімкнути її); це ввімкнено за замовчуванням у Python 3.3 та новіших версіях.

Якщо ви покладалися на порядок ключів у наборі Python, то не робіть цього. Python використовує хеш-таблицю для реалізації цих типів, і їх порядок залежить від історії вставки та видалення , а також випадкового насіння хешу. Зверніть увагу, що в Python 3.5 та старіших версіях це стосується і словників.

Також див. object.__hash__()Документацію до спеціальних методів :

Примітка : За замовчуванням __hash__()значення str, bytes та datetime об’єкти “соляться” з непередбачуваним випадковим значенням. Хоча вони залишаються постійними в межах окремого процесу Python, їх не можна передбачити між повторними викликами Python.

Це призначено для захисту від відмови в обслуговуванні, спричиненої ретельно підібраними вхідними даними, які використовують найгіршу ефективність вставки дикту, складність O (n ^ 2). Детальніше див. На http://www.ocert.org/advisories/ocert-2011-003.html .

Зміна хеш-значень впливає на порядок ітерацій диктів, наборів та інших відображень. Python ніколи не давав гарантій щодо цього впорядкування (і воно зазвичай варіюється між 32-бітовою та 64-бітною збірками).

Дивіться також PYTHONHASHSEED.

Якщо вам потрібна стабільна хеш-реалізація, ви, мабуть, хочете поглянути на hashlibмодуль ; це реалізує криптографічні хеш-функції. Проект pybloom використовує цей підхід .

Оскільки зсув складається з префікса та суфікса (початкове значення та кінцеве значення XORed відповідно), ви не можете просто зберегти зсув, на жаль. Позитивом є те, що це означає, що зловмисники не можуть легко визначити зсув за допомогою атак часу.


9
Я очікував би, що це відобразиться в документах hash (), а не лише в __hash __ (). +1 за чудову відповідь. ps Хіба hashlib не є надмірним для некриптографічного використання хеш-функцій?
redlus

1
pybloom використовує функції hashlib. Але якщо ви хочете щось швидше, ви можете перевірити pyhash .
Håken Lid

3
Чому документація називає це, disableвстановлюючи значення 0? Я не бачу ефективної різниці у встановленні будь-якого старого стабільного числа насіння, якщо я чогось не пропустив. Я маю на увазі, що коли я використовую, PYTHONHASHSEED=12345я отримую один і той же хеш для рівних рядків навіть у сеансах - те саме відбувається, коли я використовую PYTHONHASHSEED=0- хеш для рівних рядків буде однаковим протягом сеансів (хоч і відрізняється від 12345, але це очевидно, ось так насіння робота).
blubberdiblub

@blubberdiblub: 0оскільки взагалі немає насіння, а хеші для об'єктів рівні тим, що генеруються у попередній версії Python без підтримки хеш-насіння.
Мартін Пітерс

1
@MartijnPieters, що означає для постраждалих хешів відсутність "насіння взагалі"? Яка семантична чи якісна різниця у наявності насіння, скажімо, 12345, окрім того, що він створює два окремі набори сеансів, між якими значення хешів відрізняються, і окрім PYTHONHASHSEED = 0, що дорівнює старим версіям? Чи можете ви зв'язати мене з певним фрагментом вихідного коду? Я думаю, мій сенс полягає в тому, що якщо такої різниці немає, я б назвав це початковим кодом 0 та старшими версіями Python, що підтримують лише початкове число 0. Документація, як вона стоїть зараз, для мене досить заплутана.
blubberdiblub

10

У Python 3 рандомізація хешів увімкнена за замовчуванням . Це функція безпеки:

Рандомізація хеш-класів призначена для захисту від відмови в обслуговуванні, спричиненої ретельно підібраними вхідними даними, які використовують найгірші показники конструкції дикту

У попередніх версіях з 2.6.8 ви могли ввімкнути його в командному рядку за допомогою -R або опції середовища PYTHONHASHSEED .

Ви можете вимкнути його, встановивши PYTHONHASHSEEDнуль.


-9

hash () - це вбудована функція Python, і використовуйте її для обчислення хеш-значення для об'єкта , а не для рядка чи num.

Ви можете побачити деталі на цій сторінці: https://docs.python.org/3.3/library/functions.html#hash .

і значення hash () походить від методу __hash__ об'єкта. У документі сказано наступне:

За замовчуванням значення хеш () str, bytes та datetime об'єктів "соляться" з непередбачуваним випадковим значенням. Хоча вони залишаються постійними в межах окремого процесу Python, їх не можна передбачити між повторними викликами Python.

Ось чому у вас є різне хеш-значення для одного рядка в різній консолі.

Те, що ви впроваджуєте, не є хорошим способом.

Коли ви хочете обчислити хеш-значення рядка, просто використовуйте hashlib

hash () має на меті отримати хеш-значення об'єкта, а не заважати.


6
hash()ідеально підходить для рядкових або числових значень. Ви плутаєте це з __hash__користувальницький метод, який використовується приhash() забезпечити власну реалізацію хеш - значення.
Мартін Пітерс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.