Захист мобільного додатку від перехвату трафіку (MITM)
На корпоративному Wi-Fi або при підключенні до точки доступу зловмисника Burp Suite перехоплює весь HTTPS-трафік додатку за 2 хвилини — потрібно просто встановити прокси та додати сертифікат у довірені. Більшість додатків довіряє будь-якому сертифікату, підписаному системною ЦС. Це і є MITM.
TLS як базовий рівень
HTTPS без Certificate Pinning захищає тільки від пасивного прослуховування, але не від атаки з підміною сертифіката. Атакуючий з можливістю додати свою ЦС у довірені (через 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 — закріплення конкретного сертифіката або публічного ключа сервера в додатку. Навіть якщо атакуючий підсунув свою ЦС, з'єднання розірветься: сертифікат сервера не збігається з закріпленим.
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=") // резервний pin
.build()
)
.build()
Два pins обов'язково — основний та резервний. Якщо тільки один, при ротації ключа додаток перестає працювати у всіх користувачів до оновлення.
На 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 та звітування про порушення.
Обхід та контрмери
Стандартний Frida-скрипт ssl-unpinning.js хукує TrustManagerImpl.checkServerTrusted(), SSLContext.init(), OkHttp CertificatePinner.check() та обходить більшість популярних реалізацій за секунди. Це не означає, що pinning безполезний — він піднімає поріг входу з «скачав Burp» до «встановив Frida на рутований пристрій та підібрав скрипт».
Посилення: реалізувати pinning в нативному коді (JNI), не використовувати стандартні API, які хукають автоматичні скрипти, додати детектування отладчика перед мережевими запитами.
Network Security Config на Android 7+. trust-anchors можна обмежити тільки системними ЦС (прибравши користувацькі). Додаток не буде довіряти сертифікату, встановленому користувачем через настройки — Burp прокси одразу перестає працювати без рута.
Certificate Transparency
CT логи — публічні реєстри всіх виданих сертифікатів. Браузери вимагають CT SCT (Signed Certificate Timestamp) для доверу. На мобільних — опціональна додаткова перевірка: переконатися, що сертифікат сервера присутній у CT логах. Захищає від видання тіньових сертифікатів для домену.
HPKP та його проблеми
HTTP Public Key Pinning (HPKP) — серверний заголовок з закріпленими ключами. Браузери його підтримували, потім убрали через ризики (можна заблокувати сайт навсіки неправильною конфігурацією). На мобільних — не використовуємо, пиннинґ робимо на клієнті.
Термін реалізації pinning'у з двома резервними ключами, нативної реалізації на Android та iOS, плюс конфігурація network_security_config — 2–3 дні з тестуванням сценаріїв ротації сертифікатів.







