Что такое SSL pinning, зачем он нужен и как работает?
Что проверяют
Знаете ли вы, что на Android закрепление это прежде всего вопрос конфигурации, а не своего кода, и понимаете ли, какой ценой оно даётся релизному циклу.
Как отвечать
Начать стоит с модели угрозы: HTTPS доверяет любому корню из системного хранилища, поэтому корпоративный прокси, вручную установленный корень или скомпрометированный удостоверяющий центр позволяют читать трафик. Закрепление сужает доверие до известных заранее ключей.
Два рабочих способа:
- декларативно, в конфигурации сетевой безопасности: домен, набор пинов с отпечатками публичного ключа и срок действия набора. Плюс в том, что правило действует на весь стек, который опирается на системное доверие, включая популярный HTTP-клиент;
- кодом, когда правила разные для разных хостов или пины подтягиваются из удалённого конфига.
val pinner = CertificatePinner.Builder()
.add("api.example.com", "sha256/AAA...", "sha256/BBB...")
.build()Дальше про эксплуатацию: закрепляют публичный ключ, а не лист; держат запасной пин; в отладочных сборках отдельной конфигурацией разрешают пользовательские сертификаты, чтобы снифферы работали у тестировщиков и не работали в релизе.
Где спотыкаются
Думают, что конфигурация закрывает весь трафик приложения. Она действует там, где используется системное доверие. Свой менеджер доверия, веб-вью и встроенные сетевые движки кроссплатформенных фреймворков живут мимо неё, и закрепление там делается отдельно.
Ставят единственный пин на листовой сертификат и узнают о продлении по обвалу метрик. Починить нечем: сервер уже отдаёт новый сертификат, старые сборки его отвергают. Отсюда запасной ключ и осознанный срок действия набора, после которого проверка намеренно перестаёт применяться.
Путают закрепление с защитой от анализа. На устройстве с root проверка снимается перехватчиком, а иногда достаточно подменить конфигурацию в пересобранном пакете. Это мера против чужого посередине у обычного пользователя, а не против исследователя с устройством в руках.