Криптография правильно: не изобретайте свою

В криптографии «работает на моих тестах» не значит «безопасно».

Don't roll your own crypto — правило не изобретать собственные шифры и протоколы, а использовать проверенные временем библиотеки и стандарты.

Почему своя криптография — почти всегда ошибка

Шифр может корректно шифровать и расшифровывать и при этом быть катастрофически нестойким: уязвимость прячется в математике и в тонкостях реализации, которые не видны по функциональным тестам. Проверенные алгоритмы (AES, ChaCha20, RSA, кривые) годами ломают лучшие криптографы — и не сломали. Самописный шифр такой проверки не проходил. То же касается «своих» режимов, паддингов и генераторов — почти все классические провалы именно там.

Здесь стоит развеять интуицию, которая часто подводит. Кажется: «я придумал хитрое преобразование, его же никто не знает — значит, не взломает». Это и есть запрещённый принцип «безопасность через неясность»: стойкость не должна зависеть от того, что алгоритм секретен. Стойкая криптография устроена наоборот — алгоритм публичен и многократно проанализирован, секретен только ключ. Если ваша схема держится лишь на том, что её код не опубликован, она рухнет, как только код станет известен (а он станет: декомпиляция, утечка, инсайдер).

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

Используйте высокоуровневые библиотеки

Чем меньше криптографических решений вы принимаете руками, тем лучше. Современные библиотеки дают «коробочные» функции с безопасными умолчаниями: одна функция «зашифровать», одна «расшифровать», правильный режим и IV под капотом. Это и есть главный практический совет урока: ищите самый высокоуровневый интерфейс, который решает вашу задачу целиком, и не спускайтесь к «кубикам» вроде голого блочного шифра, если только вы не криптограф, отвечающий именно за это. Чем выше уровень API, тем меньше у вас возможностей выбрать небезопасный режим, забыть про аутентификацию или переиспользовать nonce.

// Уязвимо: ручная сборка из низкоуровневых примитивов
cipher = AES.new(key, AES.MODE_ECB)   // ECB раскрывает повторяющиеся блоки!
ct = cipher.encrypt(pad(plaintext))    // без аутентификации -> подмена незаметна

// Безопасно: высокоуровневый AEAD с безопасными умолчаниями
ct = secretbox.encrypt(plaintext, key)   // случайный nonce + аутентификация внутри
pt = secretbox.decrypt(ct, key)          // подмена шифртекста будет обнаружена

Аутентифицированное шифрование (AEAD)

Просто «зашифровать» мало: атакующий, не зная содержимого, может изменить шифртекст, и расшифровка молча выдаст искажённые данные. AEAD (AES-GCM, ChaCha20-Poly1305) шифрует и добавляет тег целостности: при любой подмене расшифровка завершится ошибкой. Это безопасный дефолт для шифрования данных.

Чтобы понять, почему одного шифрования недостаточно, вспомните, что шифрование скрывает смысл, но не запрещает менять биты. В некоторых режимах перевернуть конкретный бит шифртекста — значит предсказуемо перевернуть соответствующий бит расшифрованного текста. Атакующий, не зная ключа, может прицельно поправить, скажем, поле «сумма» или «роль» в зашифрованном сообщении. AEAD закрывает это тем, что вместе с шифртекстом считает криптографический тег по содержимому: изменили хоть один бит — тег не сойдётся, и библиотека вернёт ошибку вместо «починенных» данных.

У AEAD есть и приятный бонус — «связанные данные» (associated data): к шифртексту можно привязать незашифрованный, но аутентифицированный контекст, например идентификатор получателя или версию формата. Эти данные передаются открыто, но подменить их незаметно нельзя — они тоже входят в тег. Это удобно, когда часть метаданных должна оставаться читаемой, но не должна подделываться.

Что и чем защищать

ЗадачаПравильный инструмент
пароли пользователейargon2/bcrypt (хеш, не шифрование!)
данные «в покое» (диск, БД)AEAD (AES-GCM) с ключом из KMS
данные «в пути» (сеть)TLS
проверка целостностиHMAC / подпись

Частая путаница — пытаться «зашифровать» пароли. Пароли не шифруют (это обратимо), а хешируют специальным алгоритмом — см. раздел про аутентификацию.

Как работает под капотом: nonce и управление ключами

Большинство режимов требуют уникальный nonce/IV на каждое шифрование одним ключом; повтор nonce в GCM раскрывает данные. Поэтому nonce генерируют криптостойким рандомом и никогда не переиспользуют. Не менее важно управление ключами: ключ хранят в KMS/хранилище, ротируют, не кладут рядом с шифртекстом и не зашивают в код. Самый стойкий шифр бесполезен, если ключ лежит в репозитории.

Стоит отдельно понять, почему повтор nonce так опасен. Поточные режимы превращают ключ и nonce в псевдослучайный поток, которым «маскируют» данные. Если для двух разных сообщений использован один и тот же ключ и nonce, поток маскировки одинаков — и математическая разница двух шифртекстов выдаёт разницу двух открытых текстов, постепенно раскрывая содержимое и даже подрывая аутентификацию. Именно поэтому nonce обязан быть уникальным: его не обязательно скрывать, но нельзя повторять. На практике его либо берут из CSPRNG достаточной длины, либо ведут как монотонный счётчик, гарантирующий неповторяемость.

Управление ключами — отдельная дисциплина с понятной целью: ограничить «радиус поражения» при компрометации. Помогают разделение ключей по назначению (один — для платежей, другой — для сессий), оболочечное шифрование (данные шифруются ключом данных, а сам ключ данных — мастер-ключом из KMS) и регулярная ротация. Тогда утечка одного ключа не раскрывает всё сразу, а замена ключа не требует перешифровать терабайты — достаточно перевыпустить обёрнутый ключ данных.

Частые ошибки

  • Свой шифр или протокол. Берите стандарт и проверенную библиотеку.
  • Режим ECB. Раскрывает структуру данных; используйте AEAD.
  • Шифрование без аутентификации. Допускает незаметную подмену; нужен AEAD/HMAC.
  • Повтор nonce/IV, ключ рядом с данными. Сводит стойкость на нет.

Итоги

  • Не изобретайте криптографию: используйте проверенные библиотеки и стандарты.
  • Шифруйте с аутентификацией (AEAD), чтобы ловить подмену.
  • Пароли хешируют, данные шифруют, целостность проверяют HMAC/подписью.
  • Уникальный nonce и грамотное управление ключами критичны.
Проверьте себя
1. Почему не следует писать собственную криптографию?
AЭто слишком долго программировать
BСтойкость зависит от тонкой математики и реализации, не проверяемых функциональными тестами; проверенные алгоритмы годами выдерживают атаки экспертов
CСвои шифры медленнее
DЭто запрещено законом
2. Что даёт аутентифицированное шифрование (AEAD)?
AСжатие данных
BШифрование вместе с тегом целостности: подмена шифртекста обнаруживается при расшифровке
CУскорение шифрования
DВозможность восстановить ключ
3. Как правильно защищать пароли пользователей?
AШифровать их AES, чтобы при необходимости расшифровать
BХешировать специальным медленным алгоритмом (argon2/bcrypt) — это необратимо
CХранить в открытом виде под HTTPS
DПодписывать их HMAC