Защита мобильного приложения от перехвата трафика (MITM)
На корпоративном Wi-Fi или при подключении к точке доступа злоумышленника Burp Suite перехватывает весь HTTPS-трафик приложения за 2 минуты — нужно просто установить прокси и добавить сертификат в доверенные. Большинство приложений доверяет любому сертификату, подписанному системным CA. Это и есть MITM.
TLS как базовый уровень
HTTPS без Certificate Pinning защищает только от пассивного прослушивания, но не от атаки с подменой сертификата. Атакующий с возможностью добавить свой CA в доверенные (через MDM, через социальную инженерию, на корпоративном устройстве) читает весь трафик.
Минимальная гигиена: TLS 1.2 как минимум, TLS 1.3 как цель. Отключить устаревшие cipher suites (RC4, 3DES, NULL). На Android через network_security_config.xml:
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>
cleartextTrafficPermitted="false" блокирует HTTP. Обязательно для любого приложения, работающего с данными пользователей.
Certificate Pinning
Pinning — закрепление конкретного сертификата или публичного ключа сервера в приложении. Даже если злоумышленник подсунул свой CA, соединение разорвётся: сертификат сервера не совпадает с закреплённым.
Public key pinning vs certificate pinning. Пиннинг сертификата — проще, но при ротации сертификата нужно обновлять приложение. Пиннинг публичного ключа — привязка к SubjectPublicKeyInfo хэшу: ключ можно использовать при выпуске нового сертификата того же ключа. Рекомендую пиннинг ключа.
На Android через OkHttp:
val client = OkHttpClient.Builder()
.certificatePinner(
CertificatePinner.Builder()
.add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.add("api.example.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=") // backup pin
.build()
)
.build()
Два пина обязательно — основной и резервный. Если только один, при ротации ключа приложение перестаёт работать у всех пользователей до обновления.
На iOS через URLSession с кастомным URLSessionDelegate:
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard let serverTrust = challenge.protectionSpace.serverTrust,
let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let publicKey = SecCertificateCopyKey(certificate)
// сравниваем с закреплённым ключом
}
TrustKit (iOS) и Conscrypt (Android) — библиотеки, упрощающие реализацию. TrustKit поддерживает конфигурацию через Info.plist и репортинг нарушений.
Bypass и контрмеры
Стандартный Frida-скрипт ssl-unpinning.js хукает TrustManagerImpl.checkServerTrusted(), SSLContext.init(), OkHttp CertificatePinner.check() и обходит большинство популярных реализаций за секунды. Это не значит, что pinning бесполезен — он поднимает порог входа с «скачал Burp» до «установил Frida на рутованное устройство и подобрал скрипт».
Усиление: реализовывать pinning в нативном коде (JNI), не использовать стандартные API которые хукают автоматические скрипты, добавить детектирование отладчика перед сетевыми запросами.
Network Security Config на Android 7+. trust-anchors можно ограничить только системными CA (убрав пользовательские). Приложение не будет доверять сертификату, установленному пользователем через настройки — Burp прокси сразу перестаёт работать без рута.
Certificate Transparency
CT логи — публичные журналы всех выданных сертификатов. Браузеры требуют CT SCT (Signed Certificate Timestamp) для доверия. На мобильных — опциональная дополнительная проверка: убедиться, что сертификат сервера присутствует в CT логах. Защищает от выпуска теневых сертификатов для домена.
HPKP и его проблемы
HTTP Public Key Pinning (HPKP) — серверный заголовок с закреплёнными ключами. Браузеры его поддерживали, потом убрали из-за рисков (можно заблокировать сайт навсегда неправильной конфигурацией). В мобильных приложениях — не используем, пиннинг делаем на клиенте.
Срок реализации pinning'а с двумя резервными ключами, нативной реализацией на Android и iOS, плюс конфигурация network_security_config — 2–3 дня с тестированием сценариев ротации сертификатов.







