Информационная безопасность

Хеширование и цифровые подписи

2012 год. LinkedIn взломан. 117 млн паролей в MD5 без соли. Crackers с GPU перебирают 8 млрд хэшей в секунду. 90% паролей взломано за 24 часа. Через 10 лет эти данные продают в Telegram. MD5 и SHA-1 не для паролей. Никогда. Bcrypt спроектирован для медленности. Это не баг bcrypt - это фича. SHA-256 спроектирован для скорости - и это его особенность для систем целостности. Разница между «хэш для целостности» и «хэш для паролей» - одно из ключевых различений в secure engineering.

  • Git: SHA-256 для content addressing каждого коммита - имя объекта в репозитории это и есть его хэш
  • JWT: HMAC-SHA256 для stateless session tokens в каждом web API - HS256 в заголовке токена
  • TLS 1.3: Ed25519 для подписи handshake параметров + certificate chain verification при каждом HTTPS-соединении
  • Docker Hub: cosign подписывает образы через Ed25519 для supply chain security - `docker trust inspect`

SHA-256: скорость как особенность, не баг

**2012 год. LinkedIn взломан. 117 млн паролей хранились в MD5 без соли.** Crackers с GPU перебирали 8 млрд хэшей в секунду. 90% паролей взломано за 24 часа. Через 10 лет эти данные продавали в Telegram. MD5 спроектирован для скорости - и именно это его убивает для паролей. SHA-256 тоже спроектирован для скорости. Понять разницу между «хэш для целостности» и «хэш для паролей» - значит понять архитектуру безопасности современных систем.

**SHA-256 (Secure Hash Algorithm 256-bit)** - детерминированная функция: одинаковый вход всегда даёт одинаковый выход. Свойства: одностороннесть (нельзя восстановить вход по выходу), стойкость к коллизиям (нельзя найти два разных входа с одинаковым хэшем), лавинный эффект (изменение 1 бита меняет ~50% битов выхода). Построен на конструкции Меркла-Дамгора: входные данные делятся на блоки по 512 бит, каждый блок обрабатывается функцией сжатия с состоянием. Семейство SHA-2 (SHA-256, SHA-512) доминирует в production. SHA-3 (Keccak, губчатая конструкция) - альтернатива без уязвимостей length extension.

**SHA-256 в ML: content-addressed кэш весов модели.** Hugging Face хранит все model weights по SHA-256 хэшу файла. `~/.cache/huggingface/hub/models--bert-base/blobs/<sha256>`. Если файл изменился (corruption, MITM) - хэш не совпадёт с manifest, загрузка упадёт с ошибкой. Та же схема в Docker layers: каждый слой - это SHA-256 tar-архива. `docker pull` проверяет каждый слой.

Почему SHA-256 не подходит для хэширования паролей, хотя и является криптографически стойкой функцией?

bcrypt и Argon2: намеренная медленность

**bcrypt с cost=12: 300 мс на хэш. SHA-256: 0.001 мс.** Attacker с RTX 4090: 100 млрд SHA-256/сек против 52 000 bcrypt/сек. Разница в 2 млн раз в пользу bcrypt при атаке. Это не баг bcrypt - это архитектурное решение. Slow key derivation functions (KDF) спроектированы так, чтобы легитимный пользователь ждал 200-300 мс (незаметно), а перебор миллиарда паролей занял бы годы. Argon2 (PHC 2015, memory-hard) идёт дальше: требует много RAM, что делает GPU и ASIC практически бесполезными.

**bcrypt**: основан на блочном шифре Blowfish. Формат: `$2b$12$<22 байта соли><31 байт хэша>`. Число после `$2b$` - cost factor: реальное число итераций = $2^{cost}$. При cost=12 выполняется 4096 итераций. Соль (16 байт) встроена в хэш - повторная атака rainbow table невозможна. **Argon2id** (рекомендован OWASP): memory-hard (параметры memory, time, parallelism). При memory=64MB даже на GPU каждый хэш требует 64 МБ RAM - параллелизм ограничен физической памятью. **PBKDF2**: FIPS 140-2 approved, используется в iOS Keychain и Django. Менее стойкий к GPU чем Argon2.

**Никогда не использовать SHA-256(password) или MD5(password) для хранения паролей.** Даже с солью - SHA-256 слишком быстрый. Только bcrypt (legacy) или Argon2id (новые системы). Cost factor для bcrypt должен быть не ниже 12. Для Argon2id - ориентироваться на ~300 мс на сервере при current hardware, пересчитывать раз в несколько лет по мере роста мощности CPU.

Argon2id имеет параметр memory=64MB. Почему это делает GPU-атаки менее эффективными по сравнению с bcrypt?

HMAC: хэш с ключом для аутентификации сообщений

**«Почему нельзя просто SHA-256(key + message)?»** Ответ: length extension attack. Если хэш построен на конструкции Меркла-Дамгора (SHA-256 так и устроен), атакующий знающий SHA-256(secret + message) может вычислить SHA-256(secret + message + attacker_data) без знания secret. AWS SigV4 в 2010-х был уязвим к похожей атаке в ранних версиях. HMAC решает это двойным хэшированием с разными padding: $HMAC(K, m) = H((K \oplus opad) \| H((K \oplus ipad) \| m))$. Атака на внутренний хэш не даёт возможности подделать внешний.

**HMAC (Hash-based Message Authentication Code)** гарантирует целостность и аутентичность сообщения. Отличие от обычного хэша: без знания ключа K нельзя создать корректный MAC. Применения: JWT (HS256 = HMAC-SHA256 для stateless токенов), AWS SigV4 (подпись API-запросов), webhook verification (GitHub, Stripe подписывают тело запроса HMAC), TOTP/HOTP (двухфакторная аутентификация). **Timing attack**: сравнение HMAC через `==` уязвимо - атакующий измеряет время и определяет совпадающие prefix-ы. Нужен `hmac.compare_digest()` (constant-time comparison).

**HMAC в LLM API**: каждый запрос к OpenAI, Anthropic, AWS Bedrock подписывается HMAC. Ключ API - это symmetric secret, разделённый между клиентом и сервером. AWS SigV4 строит HMAC-SHA256 из canonical request (метод + path + параметры + headers + body hash) - это защищает от replay attacks и подмены любого компонента запроса.

Почему сравнение HMAC через стандартный оператор == уязвимо к атаке, и как её предотвратить?

Цифровые подписи: Ed25519 и доверие без центра

**Цифровая подпись подписывает не документ. Она подписывает хэш документа.** Если хэш-функция имеет коллизии - подпись теряет смысл. SHA-1 сломан для подписей именно по этой причине: в 2017 году Google продемонстрировал SHAttered - два разных PDF с одинаковым SHA-1. Подпись одного документа стала действительной для другого. Ed25519 (EdDSA на кривой Curve25519) - доминирующий алгоритм подписей в 2020-х: быстрее RSA-2048 в 40-50 раз, нет уязвимости к timing attacks, нет случайности в алгоритме подписи (в отличие от ECDSA, где слабый RNG сломал PS3 в 2010 году).

**Схема Ed25519**: подпись производится приватным ключом (32 байта), верификация - публичным (32 байта). Операция Sign: $Sig = EdDSA_{sk}(SHA{-}512(message))$. Verify: проверка что $Sig$ был создан владельцем $pk$ для данного $message$. **Важное отличие от RSA**: Ed25519 - не шифрование и дешифрование приватным ключом. Это схема Шнорра на эллиптической кривой - математически совершенно другая конструкция. Ментальная модель «подпись = шифрование приватным ключом» верна для RSA, но ломается для Ed25519 (95% современных систем). **Цепочка сертификатов (X.509)**: Certificate Authority (CA) подписывает публичный ключ сервера - так браузер доверяет незнакомому серверу.

**Применения Ed25519 в production**: Git commit signing (SSH-ключи с Ed25519, `git config gpg.format ssh`), Docker image signing (cosign для supply chain security), TLS 1.3 handshake (сервер подписывает DH параметры - защита от MITM), SSH аутентификация (`ssh-keygen -t ed25519`), JWT RS256/ES256 для stateful подписей (когда HS256 недостаточно из-за symmetric key). LLM API JWT tokens используют RSA-2048 или Ed25519 в зависимости от провайдера.

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

Эта модель верна только для RSA, и только при определённом padding. Ed25519 (доминирующий алгоритм сегодня) использует схему Шнорра на эллиптической кривой - математически это не шифрование/дешифрование

Для RSA: Sign(m) = m^d mod n, Verify(s) = s^e mod n - похоже на шифрование наоборот. Но Ed25519 Sign вычисляет точку на кривой через hash(key||message) и scalar multiplication - нет операции, аналогичной шифрованию. Ментальная модель RSA ломается при переходе к ECC-подписям и создаёт ошибки в понимании безопасности.

Почему в ECDSA (и PS3 был взломан) недостаточная случайность при подписи катастрофична, а в Ed25519 этой проблемы нет?

Ключевые идеи

  • **SHA-256** - быстрый хэш для целостности (Git objects, Merkle trees, file checksums). Не для паролей: GPU перебирает 100 млрд хэшей/сек. Лавинный эффект: 1 бит изменения -> ~50% бит в выходе
  • **bcrypt/Argon2id** - намеренно медленные KDF для паролей. bcrypt cost=12 -> ~300 мс/хэш, 52 000 хэшей/сек против 100 млрд у SHA-256. Argon2id memory-hard: GPU ограничен физической памятью
  • **HMAC** - keyed hash для аутентификации сообщений. Двойное хэширование защищает от length extension attacks. constant-time comparison - обязателен для защиты от timing side-channel
  • **Ed25519** - доминирующий алгоритм цифровых подписей: быстрее RSA в 40x, нет зависимости от RNG (детерминированный nonce). Git commit signing, TLS, Docker cosign - всё использует Ed25519 или его семейство

Пересечения с другими областями

Хэш-функции и подписи пронизывают все уровни современных систем - от протоколов до ML-инфраструктуры

  • Теория информации и энтропия — SHA-256 как функция максимизации энтропии выхода - связь с понятиями информационной теории
  • Сетевые протоколы — HMAC в HTTP-заголовках для подписи API-запросов (AWS SigV4, webhook verification)
  • Backend transport и HTTP — JWT токены с HMAC-SHA256 - стандарт stateless аутентификации в REST API

Вопросы для размышления

  • bcrypt cost factor рекомендуется пересчитывать раз в несколько лет по мере роста мощности CPU. Какой должна быть целевая метрика для выбора нового cost - и как это соотносится с user experience?
  • Git исторически использовал SHA-1 для content addressing коммитов (сейчас переходит на SHA-256). Если найдена коллизия SHA-1 - что конкретно это позволяет сделать атакующему в контексте Git-репозитория?
  • TLS сертификат подписан цепочкой CA. Если Root CA скомпрометирован - как это влияет на безопасность уже установленных HTTPS-соединений? Что такое certificate pinning и когда он оправдан?

Связанные уроки

  • sec-01 — Основы модели угроз и CIA-триады из первого урока - фундамент для понимания зачем нужны хэш-функции и подписи
  • sec-02 — Аутентификация и авторизация - контекст применения хэширования паролей и HMAC для API-ключей
  • sec-03 — Целостность данных - ключевое свойство, которое реализуют хэш-функции и подписи
  • sec-04 — Асимметричная криптография из предыдущего урока - математическая основа цифровых подписей
  • sec-06 — PKI и сертификаты строятся поверх цифровых подписей из этого урока
  • crypto-19-hash-functions
  • crypto-28-digital-signatures
Хеширование и цифровые подписи

0

1

Войти