Хэш-функции: целостность, а не шифрование

Хэш — это односторонний отпечаток данных. Его нельзя «расшифровать», и в этом весь смысл.

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

Главное заблуждение: хэш ≠ шифрование

Шифрование обратимо: зашифровал ключом — расшифровал ключом. Хэширование необратимо: из хэша нельзя получить исходные данные в принципе, ключа здесь нет. Поэтому хэш не используют, чтобы «спрятать и потом достать» данные, — он нужен, чтобы проверять, не изменилось ли что-то.

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

  • Детерминированность. Один и тот же вход всегда даёт один и тот же хэш.
  • Лавинный эффект. Изменение одного бита входа меняет хэш до неузнаваемости.
  • Стойкость к коллизиям. Практически невозможно найти два разных входа с одинаковым хэшем.

Запустите пример — посмотрите детерминированность и лавинный эффект:

import hashlib

def h(text):
    return hashlib.sha256(text.encode()).hexdigest()

print("Одинаковый вход -> одинаковый хэш:", h("hello") == h("hello"))
print()
print("hello :", h("hello")[:32])
print("hellp :", h("hellp")[:32])   # отличие в одну букву
print("Хэши совсем разные:", h("hello") != h("hellp"))

Вывод:

Одинаковый вход -> одинаковый хэш: True

hello : 2cf24dba5fb0a30e26e83b2ac5b9e29e
hellp : fdd7585e08c4e2afd71dcabdb4636c89
Хэши совсем разные: True

Замена всего одной буквы дала абсолютно другой хэш — это лавинный эффект.

Где применяют хэши

ЗадачаКак помогает хэш
Хранение паролейхранят хэш (с солью), а не сам пароль
Проверка целостностисверяют хэш файла до и после передачи
Цифровые подписиподписывают хэш документа, а не весь документ
Дедупликация, индексыбыстро сравнивать большие данные по короткому отпечатку

Не все хэши одинаково годны

Старые функции MD5 и SHA-1 признаны небезопасными: для них научились находить коллизии. Для проверки целостности и подписей используют семейство SHA-256 и новее. А для паролей, как мы уже знаем, нужны специальные медленные функции (bcrypt/argon2), потому что обычный SHA-256 слишком быстр для этой задачи.

HMAC: хэш с секретом

Иногда нужно не просто проверить, что данные не менялись, но и что их прислал тот, у кого есть секретный ключ. Для этого есть HMAC — хэш, «подсоленный» секретным ключом. Без ключа правильный HMAC не вычислить, поэтому подделка обнаруживается:

import hmac, hashlib

key = b"secret-key"
message = b"summa=100"

signature = hmac.new(key, message, hashlib.sha256).hexdigest()
print("Подпись сообщения:", signature[:24], "...")

# Подменим сообщение — подпись больше не совпадёт
tampered = b"summa=9999"
new_sig = hmac.new(key, tampered, hashlib.sha256).hexdigest()
print("Совпала ли подпись после подмены:", hmac.compare_digest(signature, new_sig))

Вывод:

Подпись сообщения: 5c10f44770da85a6c1367d6a ...
Совпала ли подпись после подмены: False

Итог

  • Хэш необратим — это не шифрование; его нельзя «расшифровать».
  • Хорошая хэш-функция детерминирована, обладает лавинным эффектом и стойка к коллизиям.
  • MD5 и SHA-1 устарели; используйте SHA-256 и новее, а для паролей — bcrypt/argon2.
  • HMAC добавляет к хэшу секретный ключ и подтверждает не только целостность, но и источник.
Проверьте себя
1. Чем хэширование принципиально отличается от шифрования?
AНичем
BХэширование необратимо, а шифрование обратимо с помощью ключа
CХэш всегда длиннее шифротекста
DШифрование быстрее хэширования всегда
2. Что такое лавинный эффект хэш-функции?
AХэш растёт с размером входа
BИзменение даже одного бита входа полностью меняет хэш
CХэш всегда одинаков
DФункция работает лавинообразно медленно
3. Почему MD5 не стоит использовать для проверки целостности?
AОн слишком длинный
BДля него научились находить коллизии, поэтому он небезопасен
CОн шифрует, а не хэширует
DОн работает только с паролями
4. Что добавляет HMAC по сравнению с обычным хэшем?
AОбратимость
BСекретный ключ, подтверждающий, что данные от владельца ключа
CБольшую скорость
DСжатие данных
Поддержать проект