Отключить хеш-рандомизацию из программы python
Начиная с Python 3.3, алгоритм хеширования недетерминированно salted, чтобы избежать определенного вида атаки. Это хорошо для веб-серверов, но это боль при попытке отладки программы: каждый раз, когда я запускаю свой script, содержимое dict повторяется в другом порядке.
В некоторых более ранних версиях python был флаг -R
для включения хеш-рандомизации, но теперь, когда это поведение по умолчанию, флаг не был заменен его противоположностью.
Рандомизация может быть отключена установкой переменной окружения PYTHONHASHSEED
:
PYTHONHASHSEED
Если эта переменная не задана или не задана случайной, случайное значение используется для семени хэшей объектов str, bytes и datetime.
Если значение PYTHONHASHSEED установлено на целочисленное значение, оно используется как фиксированное семя для генерации хеша() типов, охватываемых хэш-рандомизацией.
Ловушка заключается в том, что эта переменная должна быть установлена перед запуском процесса python. Я попытался установить его с помощью os.putenv()
или в os.environ
, но они, похоже, не влияют на метод хэширования. Это не слишком удивительно: я бы не ожидал, что python проверит среду перед каждым набором или поиском словаря! Итак, остается вопрос:
Есть ли способ для программы python отключить собственную хэш-рандомизацию?
Ответы
Ответ 1
Я подозреваю, что это невозможно, к сожалению. Глядя на test_hash.py
, класс HashRandomizationTests
и его потомки были добавлены в фиксацию, которая ввела это поведение, Они проверяют поведение хэширования, изменяя среду и запуская новый процесс с явным набором PYTHONHASHSEED
. Вы могли бы попытаться скопировать этот шаблон, возможно.
Я также заметил, что вы сказали: "Каждый раз, когда я запускаю свой script, содержимое dict повторяется в другом порядке". - Я предполагаю, что вы знаете collections.OrderedDict
, правильно? Это обычный способ получить надежную итерацию хеширования.
Если вы хотите установить значение в среде оболочки, вы также можете просто обернуть свой вызов python в bash script, например.
#! /bin/bash
export PYTHONHASHSEED=0
# call your python program here
Это позволяет избежать необходимости манипулировать всей вашей средой, если вы в порядке с оберткой script.
Или просто просто передайте значение в командной строке:
$ PYTHONHASHSEED=0 python YOURSCRIPT.py
Ответ 2
Помимо порядка словаря, рандомизация хэшей также может нарушить существующий код, который напрямую использует hash()
. Обходной путь, который решил проблему для меня в этом случае, должен был заменить
hash(mystring)
с
int(hashlib.sha512(mystring).hexdigest(), 16)
Для Python 3 для стандартных строк потребуется преобразование типа 'mystring.encode(' utf-8 '). (Я работал с байтовыми строками.)
Обратите внимание, что диапазон чисел и наличие отрицательных чисел различны. Последний код дает гораздо больший диапазон чисел, и коллизии хэшей крайне маловероятны.
Чтобы воспроизвести тот же 64-битный диапазон, что и в hash()
, можно уменьшить количество шестнадцатеричных цифр до 16 (4 бита на цифру) и сдвинуть результат так, чтобы он начинался с наименьшего отрицательного 64-битного числа:
int(hashlib.sha256(mystring).hexdigest()[:16], 16)-2**63
В качестве альтернативы можно взять 8 байтов и использовать int.from_bytes
:
int.from_bytes(hashlib.sha256(mystring).digest()[:8], byteorder='big', signed=True)