Как избежать подмены ответов при передаче данных между сервером и клиентом
Что проверяют
Понимаете ли вы, что HTTPS защищает канал, но не защищает от того, кто подсунул свой сертификат или контролирует само устройство. Следом почти всегда спрашивают про certificate pinning и про то, что остаётся делать на рутованном телефоне.
Как отвечать
Стройте ответ слоями, от транспорта к данным:
- TLS везде: с Android 9 открытый HTTP-трафик по умолчанию запрещён, исключения описываются в
network_security_config.xmlточечно по доменам, а не флагомusesCleartextTrafficна всё приложение; - доверие к CA: с Android 7 приложение по умолчанию не доверяет сертификатам, которые установил пользователь, поэтому прокси вроде mitmproxy без правки конфига трафик не расшифрует;
- certificate pinning:
CertificatePinnerв OkHttp или<pin-set>в Network Security Config. Пинят хеш публичного ключа, держат запасной пин и срок действия набора; - подпись ответа: если важна целостность самих данных (цена, баланс, статус подписки), сервер подписывает тело асимметричным ключом, а клиент проверяет подпись открытым. Секрет для HMAC в APK хранить бессмысленно, его достанут декомпиляцией;
- защита от повторов: nonce и метка времени в запросе, сервер отбрасывает повторы;
- сервер как источник истины: всё, что касается денег и прав, решается на сервере. На рутованном устройстве с Frida клиент подменят как угодно, поэтому цель не исключить атаку, а поднять её цену: Play Integrity API для проверки приложения и устройства, обфускация R8.
Где спотыкаются
Отвечают «HTTPS, и всё». Это не спасает, если на устройстве стоит чужой корневой сертификат или приложение собрано с доверием к пользовательским CA.
Отключают проверку сертификатов «на время отладки» через TrustManager, который принимает всё, и это уезжает в релиз. Для отладки есть <debug-overrides>, который действует только в debuggable-сборке.
Пинят сертификат листа без запасного ключа. После плановой ротации приложение перестаёт ходить в сеть у всех, кто не обновился, и чинится это только новым релизом.