Автор оригинала: Doug Hellmann.
Цель:
Модуль hmac реализует хеширование с ключом для аутентификации сообщений, так как описан в RFC 2104.
Алгоритм HMAC может использоваться для проверки целостности информации, передаваемой между приложениями или хранящейся в потенциально уязвимом месте. Основная идея состоит в том, чтобы сгенерировать криптографический хэш фактических данных в сочетании с общим секретным ключом. Полученный в результате хэш можно затем использовать для проверки переданного или сохраненного сообщения для определения уровня доверия без передачи секретного ключа.
Предупреждение
Отказ от ответственности: я не эксперт по безопасности. Для получения полной информации о HMAC ознакомьтесь с RFC 2104 .
Подписание сообщений
Функция new ()
создает новый объект для вычисления подписи сообщения. В этом примере используется алгоритм хеширования MD5 по умолчанию.
hmac_simple.py
import hmac digest_maker hmac.new(b'secret-shared-key-goes-here') with open('lorem.txt', 'rb') as f: while True: block f.read(1024) if not block: break digest_maker.update(block) digest digest_maker.hexdigest() print(digest)
При запуске код считывает файл данных и вычисляет для него подпись HMAC.
$ python3 hmac_simple.py 4bcb287e284f8c21e87e14ba2dc40b16
Альтернативные типы дайджеста
Хотя криптографическим алгоритмом по умолчанию для hmac
является MD5, это не самый безопасный метод для использования. У хэшей MD5 есть некоторые недостатки, такие как коллизии (когда два разных сообщения создают один и тот же хеш). Алгоритм SHA-1 считается более сильным, и его следует использовать вместо него.
hmac_sha.py
import hmac import hashlib digest_maker hmac.new( b'secret-shared-key-goes-here', b'', hashlib.sha1, ) with open('hmac_sha.py', 'rb') as f: while True: block f.read(1024) if not block: break digest_maker.update(block) digest digest_maker.hexdigest() print(digest)
Функция new ()
принимает три аргумента. Первый – это секретный ключ, который должен совместно использоваться двумя конечными точками, которые обмениваются данными, чтобы оба конца могли использовать одно и то же значение. Второе значение – это исходное сообщение. Если содержание сообщения, которое необходимо аутентифицировать, невелико, например отметка времени или HTTP POST, все тело сообщения можно передать в new ()
вместо использования update ()
метод. Последний аргумент – это используемый дайджест-модуль. По умолчанию используется hashlib.md5
. В этом примере передается 'sha1'
, в результате чего hmac
использует hashlib.sha1
$ python3 hmac_sha.py dcee20eeee9ef8a453453f510d9b6765921cf099
Двоичные дайджесты
В предыдущих примерах для создания дайджестов для печати использовался метод hexdigest ()
. Шестнадцатеричный файл – это другое представление значения, вычисляемого методом digest ()
, которое представляет собой двоичное значение, которое может включать непечатаемые символы, включая NUL
. Некоторые веб-сервисы (Google checkout, Amazon S3) используют версию двоичного дайджеста в кодировке base64 вместо шестнадцатеричного.
hmac_base64.py
import base64 import hmac import hashlib with open('lorem.txt', 'rb') as f: body f.read() hash hmac.new( b'secret-shared-key-goes-here', body, hashlib.sha1, ) digest hash.digest() print(base64.encodebytes(digest))
Строка в кодировке base64 заканчивается новой строкой, которую часто нужно удалять при встраивании строки в заголовки http или другие контексты, чувствительные к форматированию.
$ python3 hmac_base64.py
Применение подписей сообщений
Аутентификация HMAC должна использоваться для любой общедоступной сетевой службы и в любое время, когда данные хранятся там, где важна безопасность. Например, при отправке данных через конвейер или сокет эти данные должны быть подписаны, а затем подпись должна быть проверена перед использованием данных. Приведенный здесь расширенный пример доступен в файле hmac_pickle.py
.
Первым шагом является создание функции для вычисления дайджеста для строки и простого класса, который будет создан и передан через канал связи.
hmac_pickle.py
import hashlib import hmac import io import pickle import pprint def make_digest(message): "Return a digest for the message." hash hmac.new( b'secret-shared-key-goes-here', message, hashlib.sha1, ) return hash.hexdigest().encode('utf-8') class SimpleObject: """Demonstrate checking digests before unpickling. """ def __init__(self, name): self.name name def __str__(self): return self.name
Затем создайте буфер BytesIO
для представления сокета или канала. В примере используется наивный, но простой для анализа формат потока данных. Записываются дайджест и длина данных с новой строкой. Далее следует сериализованное представление объекта, созданное pickle. Настоящая система не хотела бы зависеть от значения длины, поскольку, если дайджест неверен, вероятно, неверна и длина. Некоторая последовательность терминатора, которая вряд ли появится в реальных данных, была бы более подходящей.
Затем пример программы записывает в поток два объекта. первый записывается с использованием правильного значения дайджеста.
# Simulate a writable socket or pipe with a buffer out_s io.BytesIO() # Write a valid object to the stream: # digest\nlength\npickle o SimpleObject('digest matches') pickled_data pickle.dumps(o) digest make_digest(pickled_data) header b'%s %d\n' % (digest, len(pickled_data)) print('WRITING: {}'.format(header)) out_s.write(header) out_s.write(pickled_data)
Второй объект записывается в поток с недопустимым дайджестом, полученным путем вычисления дайджеста для некоторых других данных вместо рассола.
# Write an invalid object to the stream o SimpleObject('digest does not match') pickled_data pickle.dumps(o) digest make_digest(b'not the pickled data at all') header b'%s %d\n' % (digest, len(pickled_data)) print('\nWRITING: {}'.format(header)) out_s.write(header) out_s.write(pickled_data) out_s.flush()
Теперь, когда данные находятся в буфере BytesIO
, их можно снова прочитать. Начните с чтения строки данных с дайджестом и длиной данных. Затем прочтите оставшиеся данные, используя значение длины. pickle.load ()
может читать непосредственно из потока, но это предполагает наличие надежного потока данных, а эти данные еще недостаточно доверены, чтобы их можно было извлечь. Считывать рассол в виде строки из потока, фактически не распаковывая объект, безопаснее.
# Simulate a readable socket or pipe with a buffer in_s io.BytesIO(out_s.getvalue()) # Read the data while True: first_line in_s.readline() if not first_line: break incoming_digest, incoming_length first_line.split(b' ') incoming_length int(incoming_length.decode('utf-8')) print('\nREAD:', incoming_digest, incoming_length)
После того, как обработанные данные находятся в памяти, значение дайджеста можно пересчитать и сравнить с данными, считанными с помощью compare_digest ()
. Если дайджесты совпадают, можно доверять данным и распаковывать их.
incoming_pickled_data in_s.read(incoming_length) actual_digest make_digest(incoming_pickled_data) print('ACTUAL:', actual_digest) if hmac.compare_digest(actual_digest, incoming_digest): obj pickle.loads(incoming_pickled_data) print('OK:', obj) else: print('WARNING: Data corruption')
Выходные данные показывают, что первый объект проверен, а второй, как и ожидалось, считается «поврежденным».
$ python3 hmac_pickle.py WRITING: b'f49cd2bf7922911129e8df37f76f95485a0b52ca 69\n' WRITING: b'b01b209e28d7e053408ebe23b90fe5c33bc6a0ec 76\n' READ: b'f49cd2bf7922911129e8df37f76f95485a0b52ca' 69 ACTUAL: b'f49cd2bf7922911129e8df37f76f95485a0b52ca' OK: digest matches READ: b'b01b209e28d7e053408ebe23b90fe5c33bc6a0ec' 76 ACTUAL: b'2ab061f9a9f749b8dd6f175bf57292e02e95c119' WARNING: Data corruption
Сравнение двух дайджестов с простой строкой или сравнение байтов может использоваться в атаке по времени, чтобы раскрыть часть или весь секретный ключ путем передачи дайджестов разной длины. compare_digest ()
реализует быструю, но постоянную функцию сравнения для защиты от временных атак.
Смотрите также
- стандартная библиотечная документация для hmac
- RFC 2104 – HMAC: хеширование с ключом для аутентификации сообщений
- hashlib – модуль
hashlib
предоставляет генераторы хешей MD5 и SHA1. - pickle – Библиотека сериализации.
- WikiPedia: MD5 – Описание алгоритма хеширования MD5.
- Подписание и аутентификация запросов REST (Amazon AWS) – инструкции по аутентификации на S3 с помощью HMAC- Учетные данные, подписанные SHA1.