Hmac.new: примеры (PYTHON)
hmac.new(key: bytes, msg: bytes = None, digestmod: str = 'md5'): hmac.HMACБазовое описание функции hmac.new
Функция hmac.new() из стандартного модуля Python hmac используется для создания объектов HMAC (Hash-based Message Authentication Code). Она применяется для проверки целостности и подлинности сообщений. Механизм HMAC объединяет хеш-функцию с секретным ключом, что позволяет получать криптографическую хеш-сумму, подделать которую без знания ключа сложно.
Сигнатура функции:hmac.new(key, msg=None, digestmod='')
Аргументы:
- key (обязательный) - секретный ключ в виде байтового объекта (
bytesилиbytearray). Рекомендуется, чтобы его длина была не меньше размера выхода хеш-функции. - msg (необязательный) - исходное сообщение для аутентификации (также в байтах). Может быть
None. Сообщение можно передать позже с помощью методовupdate(). - digestmod (необязательный) - определяет используемую хеш-функцию. Может быть строкой с названием алгоритма (например,
'md5','sha256'), самим модулем хеширования (например,hashlib.sha256) или конструктором (например,hashlib.sha256). В версиях Python 3.4+ является обязательным аргументом.
Возвращаемое значение: объект hmac.HMAC, который имеет методы, аналогичные объектам модуля hashlib (update(), digest(), hexdigest()).
Простые примеры использования
Базовый пример с передачей ключа и сообщения сразу.
import hmac, hashlib
key = b'secret-key'
message = b'Important message'
h_obj = hmac.new(key, message, digestmod='sha256')
print("HMAC hex:", h_obj.hexdigest())HMAC hex: 9c7c2a3f7e... (уникальная хеш-сумма)
Постепенная передача данных с помощью update().
import hmac, hashlib
h_obj2 = hmac.new(b'key', digestmod=hashlib.sha1)
h_obj2.update(b'part1')
h_obj2.update(b'part2')
print("HMAC digest:", h_obj2.digest())
print("HMAC hex:", h_obj2.hexdigest())HMAC digest: b'\x87\x02...' (байтовое представление) HMAC hex: 8702... (шестнадцатеричное представление)
Использование различных алгоритмов хеширования.
import hmac
key = b'test'
msg = b'data'
hmac_md5 = hmac.new(key, msg, digestmod='md5')
hmac_sha512 = hmac.new(key, msg, digestmod='sha512')
print("MD5:", hmac_md5.hexdigest())
print("SHA512:", hmac_sha512.hexdigest())MD5: 0b6e... (хеш MD5) SHA512: e3a5... (хеш SHA-512)
Похожие функции в Python
В модуле hashlib существуют прямые хеш-функции (hashlib.md5(), hashlib.sha256() и др.). Они вычисляют обычную криптографическую хеш-сумму, но без использования ключа. Эти функции подходят для проверки целостности данных, но не для аутентификации источника.
Функция hmac.compare_digest(a, b) из того же модуля применяется для безопасного сравнения двух хеш-сумм или строк, чтобы противостоять атакам по времени. Ее рекомендуют использовать вместо оператора ==.
Выбор между hmac.new() и hashlib зависит от задачи: если требуется подтвердить, что данные созданы владельцем секретного ключа, используется HMAC. Для простой проверки целостности без требований к аутентификации источника может быть достаточно hashlib.
Реализации HMAC в других языках программирования
JavaScript (Node.js): Используется модуль crypto.
const crypto = require('crypto');
const hmac = crypto.createHmac('sha256', 'secret-key');
hmac.update('message');
console.log(hmac.digest('hex'));(выведет шестнадцатеричную строку)
Java: Класс javax.crypto.Mac.
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec = new SecretKeySpec("secret-key".getBytes(), "HmacSHA256");
mac.init(keySpec);
byte[] result = mac.doFinal("message".getBytes());
// преобразование result в hex(массив байтов result)
PHP: Функция hash_hmac().
$hmac = hash_hmac('sha256', 'message', 'secret-key', true); // true для сырых данных
echo bin2hex($hmac);(шестнадцатеричное представление)
Golang: Пакет crypto/hmac.
import "crypto/hmac"
import "crypto/sha256"
key := []byte("secret-key")
msg := []byte("message")
h := hmac.New(sha256.New, key)
h.Write(msg)
result := h.Sum(nil)
// fmt.Printf("%x", result)(срез байтов result)
Основное отличие от Python заключается в синтаксисе и способе указания алгоритма, но концепция идентична: ключ, сообщение и функция хеширования.
Типичные ошибки при работе с функцией
1. Передача строки вместо байтов.
import hmac
# Неправильно:
try:
h = hmac.new('mykey', 'mymsg', digestmod='sha256')
except TypeError as e:
print("Ошибка:", e)Ошибка: key: expected bytes or bytearray, but got 'str'
2. Отсутствующий или неверный аргумент digestmod в новых версиях Python.
import hmac
# В Python 3.4+ это вызовет ошибку:
try:
h = hmac.new(b'key', b'msg')
except TypeError as e:
print("Ошибка:", e)Ошибка: digestmod должен быть указан
3. Использование короткого или пустого ключа. Хотя функция отработает, это снижает криптостойкость.
import hmac
h = hmac.new(b'', b'msg', digestmod='sha256') # Пустой ключ
print("HMAC с пустым ключом:", h.hexdigest())HMAC с пустым ключом: 6f... (вычисляется, но не безопасно)
4. Попытка использовать hexdigest() или update() после вызова digest(). Объект становится "завершенным".
import hmac
h = hmac.new(b'k', b'm', digestmod='sha256')
d = h.digest()
try:
h.update(b'more') # Попытка обновить после получения дайджеста
except AttributeError as e:
print("Ошибка:", e)Ошибка: объект уже завершен
Изменения в функции в последних версиях Python
Наиболее значимое изменение произошло в Python 3.4. Аргумент digestmod перестал быть необязательным. Если его не указать, возникает исключение TypeError. Это изменение подчеркивает важность явного выбора хеш-алгоритма для безопасности.
В Python 3.8 добавлена поддержка передачи конструктора хеш-функции из модуля hashlib в качестве аргумента digestmod наряду с использованием строки. Это повышает гибкость и ясность кода.
Пример для Python 3.8+:
import hmac, hashlib
# Оба варианта корректны:
h1 = hmac.new(b'key', digestmod='sha3_256')
h2 = hmac.new(b'key', digestmod=hashlib.sha3_256)Расширенные примеры использования HMAC
1. Проверка подлинности Webhook запроса. Часто используется для верификации запросов от сторонних сервисов.
import hmac, hashlib
# Сервер генерирует подпись
secret = b'webhook-secret'
payload = b'{"event":"update","id":123}'
computed_signature = hmac.new(secret, payload, hashlib.sha256).hexdigest()
print("Серверная подпись:", computed_signature)
# Клиент проверяет подпись (имитация)
received_signature = computed_signature # Предположим, получена из заголовка
is_valid = hmac.compare_digest(received_signature, computed_signature)
print("Подпись верна?", is_valid)Серверная подпись: 1a2b3c... Подпись верна? True
2. Создание временных токенов с истекшим сроком действия (Timed HMAC). В подпись добавляется метка времени.
import hmac, hashlib, time, json
def create_token(secret, data, validity_sec=30):
expiry = int(time.time()) + validity_sec
data['exp'] = expiry
message = json.dumps(data, sort_keys=True).encode()
token_hmac = hmac.new(secret, message, hashlib.sha256).hexdigest()
return f"{expiry}.{token_hmac}"
def verify_token(secret, token):
try:
expiry_str, received_hmac = token.split('.')
expiry = int(expiry_str)
if expiry < time.time():
return False, "Token expired"
# Пересчитываем HMAC для проверки (данные - только expiry)
calc_hmac = hmac.new(secret, json.dumps({'exp':expiry}, sort_keys=True).encode(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(calc_hmac, received_hmac):
return False, "Invalid signature"
return True, "Valid"
except Exception:
return False, "Malformed token"
secret = b'super-secret'
token = create_token(secret, {'user_id': 42})
print("Токен:", token)
print("Проверка:", verify_token(secret, token))Токен: 1640995200.abc123... Проверка: (True, 'Valid') или (False, 'Token expired')
3. Построение ключевой производной функции (KDF) на основе HMAC (HKDF). Упрощенная демонстрация идеи.
import hmac, hashlib
def hkdf_extract(salt, ikm, hash_algo=hashlib.sha256):
"""Фаза извлечения"""
if salt is None:
salt = bytes([0] * hash_algo().digest_size)
return hmac.new(salt, ikm, hash_algo).digest()
def hkdf_expand(prk, info, length, hash_algo=hashlib.sha256):
"""Фаза расширения"""
hash_len = hash_algo().digest_size
n = (length + hash_len - 1) // hash_len
t = b''
okm = b''
for i in range(1, n+1):
t = hmac.new(prk, t + info + bytes([i]), hash_algo).digest()
okm += t
return okm[:length]
# Пример использования:
salt = b'random-salt'
input_key_material = b'master-key'
info = b'application context'
prk = hkdf_extract(salt, input_key_material)
derived_key = hkdf_expand(prk, info, 32) # Получаем 32 байта
print("Производный ключ (hex):", derived_key.hex())Производный ключ (hex): a1b2c3... (32 байта)