От AppSec-инженера не ждут, что он изобретёт шифр. Ждут, что он знает, какой готовый примитив выбрать, и видит типичные ошибки применения. Главное правило: не писать свою криптографию, а правильно использовать проверенные библиотеки.
Хеширование паролей
Пароль нельзя хранить ни открыто, ни зашифрованным (ключ утечёт вместе с базой), ни быстрым хешем (MD5, SHA-256): видеокарта перебирает миллиарды быстрых хешей в секунду. Нужен медленный хеш с солью:
- Argon2id: рекомендация OWASP по умолчанию; настраиваются память, число проходов, параллелизм (например 19 МиБ, 2 прохода, 1 поток как минимальная конфигурация).
- scrypt: тоже требует памяти.
- bcrypt: проверенный временем, cost 10 и выше; учитывает только первые 72 байта пароля.
- PBKDF2 с HMAC-SHA256 и большим числом итераций (порядка 600 тысяч), когда нужна сертифицированная криптография.
Соль уникальна для каждого пароля и хранится рядом с хешем: она делает бесполезными радужные таблицы и не даёт увидеть одинаковые пароли. Перец (pepper) это дополнительный секрет вне базы, например в HSM или KMS. Параметры храните вместе с хешем, чтобы со временем усиливать их и перехешировать при входе.
Шифрование
- Симметричное: один ключ. Стандарт AES-GCM или ChaCha20-Poly1305: это AEAD, шифрование плюс проверка целостности. Nonce (IV) никогда не повторяется с тем же ключом: повтор nonce в GCM катастрофичен.
- Режим ECB нельзя: одинаковые блоки дают одинаковый шифртекст. CBC без MAC уязвим к padding oracle.
- Асимметричное: пара ключей. Для шифрования и обмена ключами (RSA-OAEP, ECDH), для подписи (Ed25519, ECDSA, RSA-PSS).
- Шифрование не даёт целостности само по себе: без MAC или AEAD шифртекст можно подменить.
Хеш, HMAC и подпись
- Хеш (SHA-256, SHA-3): отпечаток данных, без ключа. Защищает от случайного повреждения, но не от подмены: атакующий пересчитает хеш.
- HMAC: хеш с секретным ключом. Проверяет, что сообщение создал владелец ключа (вебхуки, подписанные cookie). Строить MAC как
sha256(key + message)нельзя из-за атаки удлинения сообщения. - Подпись: асимметрично, проверить может любой с публичным ключом.
- Сравнивать MAC и токены за постоянное время (
hmac.compare_digest,crypto.timingSafeEqual), иначе по времени ответа можно подобрать значение побайтно.
Случайность
Для токенов, ключей, nonce используют криптографический генератор: secrets в Python, crypto.randomBytes и crypto.randomUUID в Node, SecureRandom в Java. Math.random, random и java.util.Random предсказуемы.
TLS
- Используем TLS 1.2 и 1.3, версии 1.0 и 1.1 официально выведены из употребления.
- HSTS (
Strict-Transport-Security) запрещает браузеру ходить по HTTP после первого визита. - Проверка сертификата в клиентах обязательна:
verify=FalseиrejectUnauthorized: falseпревращают TLS в декорацию. - Внутри периметра тоже шифруем: mTLS между сервисами, TLS до базы.
JWT: ловушки
JWT это base64url(header).base64url(payload).подпись. Payload не зашифрован, его прочитает любой.
alg: none: токен без подписи. Библиотека должна принимать только явно заданный список алгоритмов.- Путаница алгоритмов: сервер ждёт RS256, атакующий присылает HS256 и подписывает токен публичным ключом как HMAC-секретом. Если библиотека берёт алгоритм из заголовка, подпись сойдётся.
- Слабый секрет HS256 перебирается офлайн по любому перехваченному токену.
- Нет проверки
exp,aud,iss: токен от другого сервиса или просроченный принимается. kid,jku,x5uиз заголовка: путь к файлу или URL ключа под контролем атакующего.- Отзыв: JWT нельзя отозвать до истечения без серверного списка, поэтому access-токены живут минуты, а refresh-токены ротируются.
- Декодирование без проверки (
jwt.decode(..., verify=False),jwt.decodeвместоjwt.verifyв Node) частая ошибка в коде.
Частые ошибки
- MD5 или SHA-256 для паролей.
- Свой «простой» шифр или XOR.
- Фиксированный IV, ECB, ключ в коде.
- Сравнение подписи через
==. verify=False«временно».
Что спросят на собесе
- Как правильно хранить пароли и зачем соль.
- Чем HMAC отличается от хеша и от подписи.
- Что такое атака путаницы алгоритмов в JWT.
- Почему важен уникальный nonce в AES-GCM.
- Что даёт HSTS.
- Как отозвать JWT.