Друзья, привет! Сегодня разберем важную тему - защиту сетевых запросов с помощью
certificate pinning. Это обязательная защита для любого приложения, которое работает с пользовательскими данными.
Зачем это нужно:
Основная цель - защита от атак «человек посередине» (Man-in-the-Middle). Злоумышленники могут перехватывать ваши запросы, тестировать уязвимости
API и даже подменять данные. Проверить уязвимость просто: достаточно запустить
Charles или
Proxyman и посмотреть, проходит ли трафик вашего приложения через прокси.
Как это работает:
При HTTPS-соединении сервер предъявляет цепочку сертификатов:
- Корневой сертификат (доверенный, уже есть в системе).
- Промежуточный сертификат.
- Листовой сертификат (конечный, именно его мы будем «пиннить»).
Что пиннить:
- Весь сертификат (просто, но негибко).
- Отпечаток (хеш) сертификата.
- Публичный ключ (самый гибкий вариант).
Практическая реализация:
Достаем сертификат сервера:
openssl s_client -connect example.com:443 -showcerts /dev/null | awk '/-----BEGIN CERTIFICATE-----/{i++} i==1 {print}' > leaf.pem
Добавляем сертификат в проект как ресурс:
Например, в папку
Certificates/example.com/leaf.der
Реализуем проверку в URLSessionDelegate:
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) {
let host = challenge.protectionSpace.host
let trustedCertificates = loadCertificates(for: host) // Ваш метод загрузки
guard let serverTrust = challenge.protectionSpace.serverTrust,
let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0),
trustedCertificates.contains(where: { SecCertificateEqual($0, serverCertificate) })
else {
return (.cancelAuthenticationChallenge, nil) // Блокируем соединение
}
return (.useCredential, URLCredential(trust: serverTrust))
}
Основная проблема:
Сертификаты имеют срок действия (обычно 1 год). Когда вы его обновляете, приложение с пиннингом перестанет работать! Решения:
- Пиннить публичный ключ (если он не меняется).
- Удаленное обновление сертификатов через CloudKit или APNS.
- Использовать несколько сертификатов «про запас».
Советы:
- Всегда тестируйте в режиме разработки с отключенным пиннингом.
- Добавьте механизм аварийного отключения пиннинга.
- Используйте готовые решения (Alamofire, TrustKit), если не хотите велосипедить.
Вывод:
Certificate pinning - это must-have для безопасности приложения. Да, это добавляет сложности, но защита пользовательских данных того стоит.