Статья Аудит криптографии в приложениях: поиск слабых алгоритмов, hardcoded ключей и ошибок реализации

Разобранный смартфон на антистатическом коврике с щупом у чипа памяти. Рядом экран ноутбука с декомпилированным кодом и выделенным жёстко заданным ключом шифрования.


Среда, 10:40. Code review мобильного приложения платёжного сервиса - 200 тысяч транзакций в сутки. Открываю декомпилированный APK в jadx, перехожу к классу PaymentCryptoHelper - и через 12 минут смотрю на AES-256 ключ, вшитый строковой константой. Формально всё по стандартам: AES, 256 бит, CBC-режим. А на практике этот ключ одинаков для каждой установки приложения и извлекается через apktool за минуту. По классификации OWASP, Cryptographic Failures (A02:2021) - вторая строчка в рейтинге рисков веб-приложений, а штрафы по GDPR за утечку данных, зашифрованных "для вида", достигают 4% глобальной годовой выручки компании. Ниже - полная методика аудита криптографии в приложениях: от статического анализа до detection-правил для SIEM и готового чеклиста для отчёта.

Таксономия криптографических уязвимостей и маппинг на MITRE ATT&CK​

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

Слабые криптографические алгоритмы (CWE-327). MD5, SHA-1, DES, RC4, 3DES - алгоритмы, чья стойкость опровергнута практическими атаками. MD5-коллизии генерируются за секунды на обычном ноутбуке, SHA-1 сломана атакой SHAttered. Если приложение использует их для хеширования паролей или проверки целостности - это иллюзия защиты, красивый фантик без конфеты. Согласно описанию OWASP A02:2021, использование deprecated hash functions вроде MD5 или SHA-1 - прямой индикатор cryptographic failure.

Hardcoded ключи и пароли (CWE-259, CWE-321). Ключи шифрования, API-токены, пароли для СУБД, зашитые в исходный код или конфигурационные файлы. Атакующий применяет технику Credentials In Files (T1552.001, Credential Access) или Private Keys (T1552.004, Credential Access) по MITRE ATT&CK и получает доступ без перебора. Здесь же - секреты в git-истории: разработчик удалил ключ из main, но он живёт в истории коммитов. Классика.

Ошибки реализации и недостаточная энтропия (CWE-331). ECB-режим шифрования, CBC без проверки целостности, слабые PRNG (Math.random() вместо SecureRandom), повторное использование IV/nonce. Эта категория самая коварная: формально алгоритм "правильный" - AES-256, RSA-2048, - но реализация обнуляет криптографическую стойкость.

Отдельно стоит техника Weaken Encryption (T1600, Defense Impairment) и подтехника Reduce Key Space (T1600.001) - целенаправленное понижение стойкости. Классический пример - FREAK-атака (CVE-2015-0204): функция ssl3_get_key_exchange в клиентском коде OpenSSL позволяла уязвимому клиенту принять слабый эфемерный RSA-ключ от сервера, что давало MitM-атакующему возможность понизить стойкость соединения до 512-битного экспортного RSA, взламываемого за часы (scope CVE - только клиентский код OpenSSL). Затронуты версии OpenSSL до 0.9.8zd, 1.0.0p и 1.0.1k.

Бизнес-логика атаки. Hardcoded ключ в мобильном приложении = расшифровка трафика всех пользователей. Небезопасные алгоритмы шифрования паролей = Password Cracking (T1110.002, Credential Access; задокументированные тесты - для Windows, но техника применима кроссплатформенно) с выходом на массовый credential stuffing. Ошибка реализации TLS = MitM с перехватом платёжных данных. Скомпрометированный разработчик или insider, намеренно ослабляющий криптографию - тоже реалистичный сценарий, и detection для него строится по тем же индикаторам.

Статический анализ криптографии: инструменты и правила поиска​

1781767017520.webp

Требования к окружению: ОС GNU/Linux (Ubuntu 22.04+) или macOS; Windows через WSL2. RAM от 4 ГБ для мобильных приложений, от 8 ГБ для крупных кодовых баз. Python 3.8+ (semgrep, TruffleHog), Java 11+ (jadx, apktool для Android APK). Все инструменты работают локально, без облачных зависимостей.

semgrep: кастомные правила для криптоаудита​

semgrep (активно поддерживается, Semgrep Inc, ~10k stars на GitHub) - SAST-инструмент с кастомными YAML-правилами. Стандартный набор p/security-audit ловит часть криптографических ошибок, но для полноценного поиска уязвимостей шифрования этого мало - нужны собственные правила.

Пример правила для поиска MD5, SHA-1 и DES в Java:
YAML:
rules:
  - id: weak-crypto-algorithm
    patterns:
      - pattern-either:
          - pattern: MessageDigest.getInstance("MD5")
          - pattern: MessageDigest.getInstance("SHA-1")
          - pattern: Cipher.getInstance("DES/...")
    message: "Weak crypto: $MATCH"
    severity: ERROR
    languages: [java]
Запуск: semgrep --config ./crypto-rules.yml ./src/ - проходит кодовую базу за минуты. Для Python аналогично: паттерны hashlib.md5(), hashlib.sha1(), from Crypto.Cipher import DES. Для Go - crypto/des, crypto/md5 в импортах.

[Применимо: code review, CI/CD pipeline, внутренний аудит. Не заменяет ручной анализ бизнес-логики шифрования.]

TruffleHog и git-secrets: охота за секретами в репозиториях​

TruffleHog (Truffle Security Co, активно поддерживается, ~17k stars на GitHub) сканирует всю историю коммитов, а не только текущее состояние. Разработчик мог удалить ключ из кода месяц назад - а он всё ещё в git log, ждёт своего часа.

Запуск по локальному репозиторию: trufflehog git file://./repo --only-verified. Флаг --only-verified проверяет, что найденные ключи реально рабочие - делает тестовый запрос к API провайдера (AWS, GCP, Stripe и т.д.).

Для pre-commit блокировки - git-secrets (AWS, активно поддерживается): git secrets --install && git secrets --register-aws. Кастомные паттерны: git secrets --add 'PRIVATE.KEY'. Каждый заблокированный коммит - событие для SOC: кто коммитил, какой секрет, куда он мог утечь до блокировки. В зрелых организациях это отдельный поток алертов в SIEM.

Ограничения статического анализа​

Статический анализ криптографии не ловит:
  • Ключи, загружаемые из Vault/KMS в рантайме - это как раз правильная реализация, ложных срабатываний не будет
  • Криптографические проблемы в скомпилированных зависимостях - для этого нужен SCA (OWASP Dependency-Check, Snyk)
  • Динамически формируемые строки: если алгоритм выбирается через String algo = getConfig("crypto.algo"), semgrep не увидит конкретное значение
  • Ошибки в бизнес-логике: например, шифрование данных на стороне клиента с передачей ключа по тому же каналу
Для полноты проверки шифрования в приложении статику дополняют динамическим анализом трафика и проверкой зависимостей. Одно без другого - половина картины.

Ошибки реализации криптографии: ECB, padding oracle, слабые PRNG​

1781767125640.webp

Эта категория - основной источник находок на реальных аудитах. Приложение использует AES-256 или RSA-2048, compliance-отчёт чист, но ошибка реализации обнуляет стойкость. Я бы сказал, что 70% моих находок за последние два года - именно отсюда.

ECB-режим шифрования. ECB (Electronic Codebook) шифрует каждый блок независимо одним ключом. Одинаковые блоки открытого текста дают одинаковые блоки шифротекста - паттерны данных сохраняются полностью. В банковском приложении это означает: повторяющиеся номера счетов или суммы видны в зашифрованном трафике без расшифровки. По сути, это шифрование "в чём мать родила" - формально одето, но всё видно. Поиск в коде - паттерн Cipher.getInstance("AES/ECB/...") для Java, AES.new(key, AES.MODE_ECB) для Python.
YAML:
  - id: aes-ecb-mode
    pattern: Cipher.getInstance("AES/ECB/$PADDING")
    message: "ECB mode preserves data patterns - use GCM"
    severity: ERROR
    languages: [java]
AES/CBC без HMAC - вектор padding oracle. AES в CBC без проверки целостности уязвим к padding oracle атаке: атакующий отправляет модифицированные шифротексты и по разнице в ответах сервера (ошибка расшифровки vs ошибка формата) восстанавливает открытый текст побайтово. CVE-2008-5161 - связанная проблема обработки ошибок в CBC-режиме SSH BPP: в OpenSSH 4.7p1 и SSH Tectia некорректная обработка ошибок MAC позволяла восстановить до 32 бит открытого текста с низкой вероятностью успеха (2⁻¹⁸) (CVSS 3.7/LOW, вектор CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N, CWE-200, CWE-329). Это не классический padding oracle в смысле Vaudenay (для которого каноничен CVE-2013-0169 / Lucky Thirteen), но демонстрирует риски CBC без аутентификации. LOW по CVSS не должен обманывать - в контексте финансовых данных даже частичная утечка открытого текста критична.

Что искать в коде: Cipher.getInstance("AES/CBC/PKCS5Padding") без последующей HMAC-верификации. Правильная замена - AES/GCM/NoPadding, где аутентификация встроена в режим.

Слабые PRNG и предсказуемые ключи. Math.random() в Java, random.random() в Python, rand() в C - не криптографически стойкие генераторы. Если они формируют IV, nonce или ключевой материал - стойкость шифрования падает до предсказуемости PRNG. Поиск: semgrep-паттерн new Random() в контексте криптографических операций, Math.random() рядом с Cipher или Key.

Уязвимость ROCA (CVE-2017-15361) - радикальный пример. Библиотека Infineon RSA library 1.02.013 для генерации ключей в TPM использовала детерминированный процесс, из-за которого RSA-ключи до 4096 бит поддавались факторизации. Затронуты Infineon TPM (версии firmware до 0000000000000422 - 4.34, до 000000000000062b - 6.43, до 0000000000008521 - 133.33), YubiKey 4 (до версии 4.3.5), Acer C720 Chromebook и другие устройства с этим чипом. Кейс показательный: даже аппаратная реализация не гарантирует безопасность при дефектной генерации ключей.

Detection криптографических аномалий в SIEM​

Аудит кода находит проблемы до продакшена. Для уже развёрнутых приложений работает мониторинг на уровне сети и инфраструктуры - и здесь пентест криптографии пересекается с задачами SOC.

Мониторинг TLS-версий и cipher suites. Подключения по TLS 1.0/1.1 или с cipher suites на базе RC4, DES, 3DES - IOC на уровне сетевого трафика. В Elastic SIEM (8.x+) фильтрация по полям tls.version и tls.cipher - нужен Filebeat с модулем TLS или Zeek/Suricata как источник парсинга handshake. В MaxPatrol SIEM - корреляция событий сетевого трафика с фильтром по версии протокола. Базовое правило:
Код:
rule: weak_tls_detected
condition:
  tls.version in ["TLSv1.0","TLSv1.1","SSLv3"]
  OR tls.cipher contains ["RC4","DES","3DES","NULL"]
severity: high
action: alert + enrich(src_ip, dst_ip, server_name)
[Ограничение: правило требует парсинга TLS handshake - стандартный netflow эту информацию не содержит. Для Splunk нужен TA для Zeek или Suricata, для RuSIEM - аналогичный парсер.]

Аномалии сертификатов. RSA-ключи менее 2048 бит на серверных сертификатах, самоподписанные сертификаты на продуктивных сервисах, истёкшие сертификаты - индикаторы TLS мисконфигурации. Периодический скан через openssl s_client -connect host:443 с парсингом вывода. Результаты - в SIEM как baseline для алертинга при деградации.

Детекция секретов в CI/CD. TruffleHog или Gitleaks в пайплайне генерируют события для SOC. Каждый заблокированный коммит с hardcoded ключом в коде - инцидент: нужно разобрать, куда секрет мог утечь до блокировки, не был ли ключ уже использован. Это отдельный поток алертов, коррелирующий с событиями доступа к репозиториям.

Insider threat / скомпрометированный разработчик. Намеренное ослабление криптографии (T1600, Defense Impairment) - редкий, но реальный сценарий. Индикатор: коммит, заменяющий AES-GCM на ECB или добавляющий hardcoded ключ в production-ветку. Detection: вебхук на diff-анализ криптографических паттернов в merge requests, интегрированный с SIEM. Коммит из нетипичной геолокации или в нерабочее время + изменение криптографической логики = алерт высокого приоритета.

Чеклист аудита криптографии в приложениях​

Готовый чеклист для включения в отчёт или передачи команде разработки. Каждый пункт - конкретное действие с командой или инструментом.
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме


Заключение​

Большинство криптографических проблем, которые я нахожу на реальных проектах, - не слабый алгоритм. Это AES-256 с ключом в git-репозитории, GCM с повторяющимся nonce, "временный" self-signed сертификат на проде третий год подряд. Индустрия зациклена на длине ключей и названиях алгоритмов, а реальные атаки эксплуатируют ошибки реализации - CWE-259 (hardcoded password, likelihood: High по классификации MITRE CWE), CWE-329 (отсутствие random IV), CWE-331 (недостаточная энтропия). Анализ безопасности криптографии на бумаге - одно, а то, что в коде, - принципиально другое. Самый результативный шаг, который я видел в командах - не покупка enterprise SAST за миллионы, а три semgrep-правила в pre-commit hook с запретом мерджа при срабатывании. Час на настройку, закрывает основную массу находок на входе. Остальное добирает TruffleHog в CI/CD и SIEM-правило на слабый TLS. Если интересно, как коллеги автоматизируют такой криптоаудит в CI/CD и делятся рабочими semgrep-конфигами под конкретные стеки, - на codeby.net есть тред по автоматизации code review с примерами для Java, Python и Go.
 
Последнее редактирование модератором:
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab