Реализация шифрования трафика в мобильном приложении
Стандартный HTTPS закрывает канал, но не решает несколько проблем: что именно передаётся в теле запроса, как защититься от компрометированного CA, и как работает шифрование для данных, которые попадают не в REST API, а в WebSocket, MQTT или BLE-канал.
TLS как основа — и почему её недостаточно
Любое продакшн-приложение использует HTTPS, но дефолтная конфигурация TLS на iOS и Android уязвима к downgrade-атакам и принимает сертификаты от сотен системных CA. Network Security Configuration на Android и App Transport Security на iOS задают минимальные требования к TLS.
Android Network Security Configuration (res/xml/network_security_config.xml):
<network-security-config>
<domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<trust-anchors>
<certificates src="@raw/my_ca"/>
</trust-anchors>
<pin-set expiration="2026-01-01">
<pin digest="SHA-256">primaryPinBase64==</pin>
<pin digest="SHA-256">backupPinBase64==</pin>
</pin-set>
</domain-config>
<base-config cleartextTrafficPermitted="false"/>
</network-security-config>
cleartextTrafficPermitted="false" блокирует HTTP на уровне ОС — ни один компонент приложения не пробьёт незашифрованный запрос. На iOS аналог — NSAllowsArbitraryLoads: false в Info.plist (дефолт с iOS 9).
Принудительный TLS 1.2+ на Android через OkHttp:
val spec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_3)
.cipherSuites(
CipherSuite.TLS_AES_128_GCM_SHA256,
CipherSuite.TLS_AES_256_GCM_SHA384,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
)
.build()
Шифрование тела запроса — когда TLS недостаточно
Если API доступно через несколько точек (CDN, API gateway, сторонние сервисы), данные могут быть видны на промежуточных узлах. End-to-end шифрование тела запроса решает это: сервер получает зашифрованный blob, промежуточные узлы видят только метаданные.
Схема с libsodium (через wrapper swift-sodium или lazysodium-android):
- Клиент при первом запуске генерирует X25519 keypair, публичный ключ регистрирует на сервере
- Сервер публикует свой публичный ключ
- Для каждого запроса:
crypto_box_easy(message, nonce, server_public_key, client_private_key)— ECDH + XSalsa20-Poly1305 - Ответ сервера зашифрован аналогично
Nonce должен быть уникальным для каждого сообщения — 24 байта случайных данных от SecRandomCopyBytes / SecureRandom. Никогда не инкрементальный счётчик без дополнительной защиты.
Шифрование WebSocket-трафика
WSS (WebSocket Secure) — TLS поверх WebSocket. Проблема та же, что с HTTPS: TLS закрывает канал, но не тело. Для чатов и финтех-приложений, где сервер не должен хранить сообщения в plaintext, нужен дополнительный уровень.
Типичный подход — Signal Protocol (libsignal): Double Ratchet + X3DH. Реализован в официальных SDK для iOS и Android. Более простая альтернатива для приложений без синхронизации между устройствами — NaCl secretbox с pre-shared key, обменянным через TLS при инициализации сессии.
BLE и локальные соединения
Bluetooth Low Energy не использует TLS. Если приложение обменивается данными с IoT-устройством через BLE, шифрование нужно реализовывать на уровне приложения.
Минимальная схема: ECDH key exchange при pairing (Curve25519), затем AES-256-GCM для каждого пакета с инкрементным nonce (защита от replay через счётчик, передаваемый в associated data). Стек: CryptoKit на iOS (нативно), Bouncy Castle или Tink на Android.
Антидетект и обфускация трафика
Для приложений в регионах с глубокой инспекцией пакетов (DPI) — отдельная задача: обфускация TLS fingerprint через изменение порядка TLS extensions, использование QUIC (HTTP/3) или domain fronting. Это выходит за рамки стандартного шифрования трафика.
Процесс работы
Аудит текущих сетевых вызовов — HTTP или HTTPS, версии TLS, наличие certificate validation. Дальше: ATS/NSC конфигурация, принудительный TLS 1.2+, certificate pinning на критичных эндпоинтах, шифрование тела там, где это требует threat model. Для нестандартных каналов (BLE, MQTT) — отдельная схема.
Сроки — 2–5 дней. Базовая конфигурация TLS + NSC/ATS — 1 день. Шифрование тела запросов с key exchange — ещё 2–3 дня.







