Безопасное хеширование с Python Hashlib

В этом руководстве вы узнаете, как создавать безопасные хэши, используя встроенные функции модуля Python hashlib.

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

Что ж, при работе над проектами Python вы, вероятно, столкнетесь со случаями, когда вас беспокоит хранение паролей и другой конфиденциальной информации в базах данных или файлах исходного кода. В таких случаях безопаснее запустить алгоритм хеширования конфиденциальной информации и сохранить хэш вместо информации.

В этом руководстве мы расскажем, что такое хеширование и чем оно отличается от шифрования. Мы также рассмотрим свойства безопасных хеш-функций. Затем мы будем использовать распространенные алгоритмы хеширования для вычисления хэша открытого текста в Python. Для этого воспользуемся встроенным модулем hashlib.

Для всего этого и многое другое, давайте начнем!

Что такое хеширование?

Процесс хеширования принимает строку сообщения и дает результат фиксированной длины, называемый хэшем. Это означает, что длина выходного хэша для данного алгоритма хеширования является фиксированной — независимо от длины входных данных. Но чем оно отличается от шифрования?

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

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

В отличие от шифрования, хеширование не является обратимым процессом, то есть мы не можем перейти от хэша к входному сообщению.

Свойства хеш-функций

Давайте быстро рассмотрим некоторые свойства, которым должны удовлетворять хеш-функции:

  • Детерминированные: хеш-функции детерминированы. Для сообщения m хэш m всегда один и тот же.
  • Устойчивость к прообразам: мы уже говорили об этом, когда говорили, что хеширование не является обратимой операцией. Свойство сопротивления прообразу утверждает, что невозможно найти сообщение m из выходного хэша.
  • Устойчивость к коллизиям: должно быть сложно (или невозможно с вычислительной точки зрения) найти две разные строки сообщений m1 и m2, такие, что хэш m1 равен хэшу m2. Это свойство называется устойчивостью к столкновениям.
  • Устойчивость ко второму прообразу: это означает, что при наличии сообщения m1 и соответствующего хэша m2 невозможно найти другое сообщение m2, такое что хеш(m1) = хэш(m2).

Модуль Python hashlib

Встроенный в Python модуль hashlib предоставляет реализации нескольких алгоритмов хеширования и дайджеста сообщений, включая алгоритмы SHA и MD5.

Чтобы использовать конструкторы и встроенные функции из модуля Python hashlib, вы можете импортировать его в свою рабочую среду следующим образом:

import hashlib

Модуль hashlib предоставляет константы алгоритмы_доступные и алгоритмы_гарантированные, которые обозначают набор алгоритмов, реализация которых доступна и гарантируется на платформе соответственно.

Таким образом, алгоритмы_гарантированные являются подмножеством алгоритмов_доступных.

Запустите Python REPL, импортируйте хэш-библиотеку и получите доступ к константам алгоритмов_доступных и алгоритмов_гарантированных:

>>> hashlib.algorithms_available
# Output
{'md5', 'md5-sha1', 'sha3_256', 'shake_128', 'sha384', 'sha512_256', 'sha512', 'md4', 
'shake_256', 'whirlpool', 'sha1', 'sha3_512', 'sha3_384', 'sha256', 'ripemd160', 'mdc2', 
'sha512_224', 'blake2s', 'blake2b', 'sha3_224', 'sm3', 'sha224'}
>>> hashlib.algorithms_guaranteed
# Output
{'md5', 'shake_256', 'sha3_256', 'shake_128', 'blake2b', 'sha3_224', 'sha3_384', 
'sha384', 'sha256', 'sha1', 'sha3_512', 'sha512', 'blake2s', 'sha224'}

Мы видим, что алгоритмы_гарантированные действительно являются подмножеством алгоритмов_доступных.

Как создавать хеш-объекты в Python

Далее давайте узнаем, как создавать хеш-объекты в Python. Мы вычислим хэш SHA256 строки сообщения, используя следующие методы:

  • Общий конструктор new()
  • Конструкторы алгоритмов

Использование конструктора new()

Давайте инициализируем строку сообщения:

>>> message = "toadmin.ru is awesome!"

Чтобы создать экземпляр хеш-объекта, мы можем использовать конструктор new() и передать имя алгоритма, как показано ниже:

>>> sha256_hash = hashlib.new("SHA256")

Теперь мы можем вызвать метод update() для хеш-объекта со строкой сообщения в качестве аргумента:

>>> sha256_hash.update(message)

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

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Unicode-objects must be encoded before hashing

Чтобы получить закодированную строку, вы можете вызвать метод encode() для строки метода, а затем использовать его в вызове метода update(). После этого вы можете вызвать метод hexdigest(), чтобы получить хэш sha256, соответствующий строке сообщения.

sha256_hash.update(message.encode())
sha256_hash.hexdigest()
# Output:'b360c77de704ad8f02af963d7da9b3bb4e0da6b81fceb4c1b36723e9d6d9de3d'

Вместо того, чтобы кодировать строку сообщения с помощью метода encode(), вы также можете определить ее как строку байтов, поставив перед строкой префикс b следующим образом:

message = b"toadmin.ru is awesome!"
sha256_hash.update(message)
sha256_hash.hexdigest()
# Output: 'b360c77de704ad8f02af963d7da9b3bb4e0da6b81fceb4c1b36723e9d6d9de3d'

Полученный хеш совпадает с предыдущим хэшем, что подтверждает детерминированность хеш-функций.

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

Чтобы убедиться в этом, давайте изменим «a» в «awesome» на «A» и вычислим хэш:

message = "toadmin.ru is Awesome!"
h1 = hashlib.new("SHA256")
h1.update(message.encode())
h1.hexdigest()
# Output: '3c67f334cc598912dc66464f77acb71d88cfd6c8cba8e64a7b749d093c1a53ab'

Мы видим, что хеш меняется полностью.

Использование алгоритмического конструктора

В предыдущем примере мы использовали универсальный конструктор new() и передали «SHA256» в качестве имени алгоритма для создания хеш-объекта.

Вместо этого мы также можем использовать конструктор sha256(), как показано ниже:

sha256_hash = hashlib.sha256()
message= "toadmin.ru is awesome!"
sha256_hash.update(message.encode())
sha256_hash.hexdigest()
# Output: 'b360c77de704ad8f02af963d7da9b3bb4e0da6b81fceb4c1b36723e9d6d9de3d'

Выходной хэш идентичен хешу, который мы получили ранее для строки сообщения «toadmin.ru is awesome!».

Изучение атрибутов хеш-объектов

Хэш-объекты имеют несколько полезных атрибутов:

  • Атрибут дайджеста_size обозначает размер дайджеста в байтах. Например, алгоритм SHA256 возвращает 256-битный хеш, что эквивалентно 32 байтам.
  • Атрибут block_size относится к размеру блока, используемому в алгоритме хеширования.
  • Атрибут name — это имя алгоритма, который мы можем использовать в конструкторе new(). Поиск значения этого атрибута может быть полезен, когда хеш-объекты не имеют описательных имен.

Мы можем проверить эти атрибуты для объекта sha256_hash, который мы создали ранее:

>>> sha256_hash.digest_size
32
>>> sha256_hash.block_size
64
>>> sha256_hash.name
'sha256'

Далее давайте рассмотрим некоторые интересные приложения хеширования с использованием модуля Python hashlib.

Практические примеры хеширования

Проверка целостности программного обеспечения и файлов

Как разработчики, мы постоянно загружаем и устанавливаем пакеты программного обеспечения. Это верно независимо от того, работаете ли вы с дистрибутивом Linux, с Windows или Mac.

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

Это также можно применить к файлам на вашем компьютере. Даже самое незначительное изменение содержимого файла резко изменит хеш, вы можете проверить, был ли файл изменен, проверив хэш.

Вот простой пример. Создайте текстовый файл my_file.txt в рабочем каталоге и добавьте в него содержимое.

$ cat my_file.txt
This is a sample text file.
We are  going to compute the SHA256 hash of this text file and also
check if the file has been modified by
recomputing the hash.

Затем вы можете открыть файл в двоичном режиме чтения (‘rb’), прочитать содержимое файла и вычислить хэш SHA256, как показано ниже:

>>> import hashlib
>>> with open("my_file.txt","rb") as file:
...     file_contents = file.read()
...     sha256_hash = hashlib.sha256()
...     sha256_hash.update(file_contents)
...     original_hash = sha256_hash.hexdigest()

Здесь переменная original_hash — это хэш my_file.txt в его текущем состоянии.

>>> original_hash
# Output: '53bfd0551dc06c4515069d1f0dc715d002d451c8799add29f3e5b7328fda9f8f'

Теперь измените файл «my_file.txt». Вы можете удалить лишние начальные пробелы перед словом «идти». 🙂

Еще раз вычислите хэш и сохраните его в переменной calculated_hash.

>>> import hashlib
>>> with open("my_file.txt","rb") as file:
...     file_contents = file.read()
...     sha256_hash = hashlib.sha256()
...     sha256_hash.update(file_contents)
...     computed_hash = sha256_hash.hexdigest()

Затем вы можете добавить простой оператор assert, который утверждает, что вычисленный_хэш равен исходному_хэшу.

>>> assert computed_hash == original_hash

Если файл изменен (что верно в данном случае), вы должны получить AssertionError:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

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

Заключение

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

  • Модуль Python hashlib предоставляет готовые к использованию реализации нескольких алгоритмов хеширования. Вы можете получить список алгоритмов, гарантированных на вашей платформе, используя hashlib.algorithms_guaranteed.
  • Чтобы создать хэш-объект, вы можете использовать универсальный конструктор new() с синтаксисом: hashlib.new(«algo-name»). В качестве альтернативы вы можете использовать конструкторы, соответствующие конкретным алгоритмам хеширования, например: hashlib.sha256() для хэша SHA 256.
  • После инициализации строки сообщения для хэширования и хэш-объекта вы можете вызвать метод update() для хеш-объекта, а затем метод hexdigest() для получения хэша.
  • Хеширование может пригодиться при проверке целостности программных артефактов и файлов, хранении конфиденциальной информации в базах данных и т. д.

Затем узнайте, как написать генератор случайных паролей на Python.