open atlas
↑ К треку
Основы безопасности SECF · 02 · 01

Хеширование против шифрования против подписи

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

SECF Middle ◷ 15 min
Уровень
ОсновыJuniorMiddleSenior

Джуниор из твоей команды выкатывает «безопасное» хранилище паролей: AES-256 поверх каждого пароля, ключ в конфиге. Ревью одобряет — AES-256, звучит надёжно. Через полгода утекает бэкап, вместе с ним утекает ключ, и каждый пароль в базе восстанавливается в открытом виде. Баг был не в слабом алгоритме. Это был полностью неверный примитив: пароли нужно хешировать, а не шифровать, именно потому что хеширование — единственная операция, которую нельзя обратить. На той же неделе другой инженер «подписывает» API-нагрузку, прогоняя её через SHA-256, — и не доказывает ничего, ведь этот хеш может пересчитать кто угодно. Три примитива, три разные гарантии, и обращение не к тому из них — один из самых частых способов, которыми в остальном аккуратные системы получают пробой.

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

Три примитива и единственное свойство, которое их разделяет

Самый быстрый способ держать их в голове — задать каждому один вопрос: можно ли вернуть оригинал?

  • Хеширование — это односторонняя функция. SHA-256("hello") всегда даёт один и тот же 256-битный дайджест, но unhash() не существует — вход уничтожен. Проверяешь, пересчитав хеш кандидата и сравнив. Его работа — целостность и отпечаток: совпали ли эти байты бит-в-бит? Он ничего не говорит о конфиденциальности (дайджест публичен) и ничего о том, кто его создал.
  • Шифрованиеобратимо при наличии ключа. encrypt(plaintext, key) даёт шифротекст, который decrypt(ciphertext, key) превращает обратно в открытый текст. Его работа — конфиденциальность: держать содержимое в секрете от всех без ключа. Само по себе классическое шифрование не доказывает, что сообщение не подменили, — поэтому современные системы используют аутентифицированное шифрование (AES-GCM, ChaCha20-Poly1305), привинчивающее тег целостности.
  • Подписьасимметричная. Подписываешь приватным ключом, а проверяет кто угодно соответствующим публичным. Её работа — аутентичность (это пришло от держателя приватного ключа) плюс целостность (и не было изменено с тех пор). Она намеренно не скрывает содержимое — подписанный JWT читается кем угодно.

Чего каждый из них не гарантирует — режимы отказа

Пробои берутся из негативного пространства: когда примитиву приписывают свойство, которое он никогда не обещал.

Хеширование — не шифрование. Хеш безопасен для публикации, но не для секрета. «Мы хешируем СНИЛСы» не защищает ничего, если пространство входов мало — атакующий хеширует все возможные значения (их порядка 10⁹) и сопоставляет за секунды. Хеширование скрывает только высокоэнтропийное и неугадываемое. Поэтому же простой хеш неверен для паролей: пароли низкоэнтропийны, так что нужен медленный, посоленный парольный хеш (bcrypt, scrypt, Argon2id), весь смысл которого — быть дорогим в вычислении, побеждая офлайн-перебор. SHA-256(пароль) — это миллиарды попыток в секунду на GPU; Argon2id с правильными параметрами — единицы.

Шифрование — не целостность и не аутентификация. Классический шифротекст в режиме CBC может быть изменён атакующим, который не может его прочитать, — переверни биты в шифротексте, перевернутся биты в расшифрованном открытом тексте (семейства атак padding-oracle и bit-flipping). «Оно зашифровано» не значит «оно неподменено», если ты не используешь AEAD-режим, который аутентифицирует. И шифрование ничего не говорит о том, кто отправил: общий ключ означает, что сообщение мог создать любой, у кого этот ключ есть.

Подпись — не шифрование. Подпись прикреплена к данным, которые остаются в открытом виде. Люди регулярно думают, что подписанный JWT «безопасен», и кладут секреты в его нагрузку — но нагрузка это просто base64, читаемый кем угодно, кто перехватит токен. Подпись защищает от подделки и подмены, но никогда от подслушивания.

Почему это работает

Почему подпись асимметрична (приватным подписываешь, публичным проверяешь), если MAC на общем ключе вроде HMAC тоже доказывает целостность? Из-за того, кто что может. С HMAC проверяющий держит тот же секрет, что и подписывающий, — значит, проверяющий тоже мог подделать сообщение; он доказывает целостность лишь между взаимно доверяющими сторонами. Цифровая подпись расщепляет ключ: только держатель приватного ключа может создать валидную подпись, а проверить может весь мир, не получая возможности подделать. Поэтому TLS-сертификаты, релизы ПО и JWT, подписанные RS256/ES256, используют асимметричные подписи — нужно, чтобы третьи стороны проверяли происхождение, не доверяя им силу подделки. HMAC (HS256) уместен только тогда, когда одна сторона и выпускает, и проверяет токен.

Выбор верного примитива под задачу

Зрелый ход — отображать требование на примитив, а не имя алгоритма на ощущение «надёжности». Пройди требование: мне нужно держать это в секрете (конфиденциальность → шифрование), доказать, что это не изменили (целостность → хеш/MAC/подпись), доказать, кто это создал (аутентичность → подпись или MAC), или хранить секрет, который мне нужно лишь проверять, никогда не считывая обратно (односторонне → парольный хеш)?

ТребованиеПримитивКонкретный выборЛовушка, которой избежать
Хранить парольМедленный парольный хешArgon2id (или bcrypt/scrypt) + соль на пользователяШифровать его (утечка ключа = открытый текст) или простой SHA-256 (перебор на GPU)
Держать данные в секрете на хранении / в передачеАутентифицированное шифрованиеAES-256-GCM или ChaCha20-Poly1305Неаутентифицированный CBC (поддаётся подмене); повтор nonce
Проверить, что файл/загрузка целыХеш (отпечаток)Дайджест SHA-256, сравнение за постоянное времяMD5/SHA-1 (коллизии); доверие хешу из того же канала
Доказать происхождение третьим сторонамАсимметричная подписьEd25519 / ECDSA (RS256/ES256 для JWT)Класть секреты в подписанную, но незашифрованную нагрузку
Защитить токен от подмены между двумя доверяющими сервисамиMAC (общий ключ)HMAC-SHA-256Считать HMAC проверяемой третьими сторонами подписью

Реальная система обычно композирует их, а не выбирает один. TLS 1.3 — протокол за каждым https:// — использует все три в одном рукопожатии: асимметричную подпись для аутентификации сертификата сервера, асимметричный обмен ключами для согласования секрета, этот секрет питает аутентифицированное шифрование сессии, а хеширование (HKDF) вплетено в вывод ключей и в транскрипт, доказывающий, что само рукопожатие не было подменено. Понимание, какой примитив делает какую работу внутри этого стека, — разница между настройкой TLS и слепым копированием.

Выбери лучший вариант

Ты хранишь пароли пользователей для системы входа. Выбери верный подход.

Викторина

Коллега говорит: «JWT подписан нашим ключом, так что класть API-токен пользователя внутрь нагрузки безопасно». В чём ошибка?

Викторина

Почему шифрование данных одним AES-256-CBC (без MAC, без AEAD) недостаточно, когда атакующий может изменить шифротекст в передаче?

Расставь шаги по порядку

Сопоставь каждое требование с его примитивом — упорядочь требования от «держать в секрете» до «доказать, кто отправил»:

  1. 1 Сделать содержимое нечитаемым для всех без ключа → шифрование
  2. 2 Обнаружить любое изменение скачанного файла → хеш (отпечаток)
  3. 3 Хранить пароль, который только проверяешь, никогда не считывая → медленный посоленный парольный хеш
  4. 4 Доказать, что сообщение пришло от конкретной стороны, с проверкой кем угодно → асимметричная подпись
Вспомните перед уходом
  1. 01
    Для хеширования, шифрования и подписи назови, что гарантирует каждый, и единственное свойство, которое их различает.
  2. 02
    Почему пароли нужно хешировать медленным посоленным хешем, а не шифровать или прогонять через простой SHA-256, и почему подписанный JWT небезопасен для секретов?
Итог

Хеширование, шифрование и подпись — три разных примитива, разделённых одним вопросом: можно ли обратить и каким ключом? Хеширование одностороннее и без ключа — покупает целостность и отпечаток, никогда конфиденциальность или происхождение. Шифрование обратимо ключом — покупает конфиденциальность, но классические режимы не дают целостности, так что тянись за аутентифицированным шифрованием (AES-GCM, ChaCha20-Poly1305). Подпись асимметрична — приватным ключом подписать, публичным проверить — покупает проверяемую третьими сторонами аутентичность плюс целостность, оставляя данные читаемыми. Пробои живут в негативном пространстве: шифровать пароли вместо хеширования (утечка ключа = открытый текст), прогонять низкоэнтропийные данные через простой SHA-256 (перебор на GPU), доверять целостность неаутентифицированному CBC (bit-flipping) или набивать секреты в подписанный, но незашифрованный JWT. Зрелый рефлекс — сначала назвать требование (секретно, цело, атрибутируемо или только-проверять-не-читать), и только потом выбирать примитив. Реальные системы вроде TLS 1.3 композируют все три; понимание, что делает каждый, и отличает настройку криптографии от слепого копирования.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 6 завершено
Связанные уроки

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.