Certificate Pinning: что это такое, механизм и способы фиксации

Автор: IT Sectr Опубликовано: 2026-03-09 Время чтения: 8 мин

Certificate Pinning — механизм фиксации сертификата или открытого ключа сервера, при котором приложение использует заранее известный отпечаток для проверки HTTPS-соединения. В отличие от стандартной цепочки доверия через CA, pinning гарантирует, что даже скомпрометированный центр сертификации не сможет выдать подменный сертификат для вашего домена. По данным OWASP MSTG (2025), Certificate Pinning входит в список обязательных controls для приложений уровня защиты L2. Реализация включает хранение хешей сертификатов в коде и проверку при каждом запросе.

Главное

  • Certificate Pinning — техника, при которой приложение доверяет только сертификату с заранее известным отпечатком, игнорируя всю цепочку CA
  • Public Key Pinning — альтернатива, фиксирующая только открытый ключ, что упрощает ротацию при смене сертификата
  • HPKP (HTTP Public Key Pinning) — устаревший стандарт на уровне HTTP-заголовков, не рекомендуется для новых проектов
  • Backup pins — резервные отпечатки, обеспечивающие непрерывность соединения при смене или истечении основного сертификата
  • Реализация на iOS через SecTrustEvaluate, на Android через CertificatePinner в OkHttp или TrustManager

Что такое Certificate Pinning?

Certificate Pinning — это техника безопасности, при которой приложение хранит отпечаток (fingerprint) доверенного сертификата и использует его как единственный критерий для установления HTTPS-соединения. В стандартной модели TLS клиент проверяет, что сертификат сервера подписан доверенным корневым CA — любым из сотен предустановленных в системе центров. Certificate Pinning заменяет эту цепочку на прямую проверку: сертификат должен совпадать с сохранённым образцом или содержать ожидаемый открытый ключ.

Проблема стандартной модели стала очевидной после инцидентов с компрометацией CA — DigiNotar (2011), Comodo (2011), TrustCor (2022). Если CA выпускает подменный сертификат для вашего домена, браузер или приложение принимает его как валидный. Certificate Pinning предотвращает эту атаку: даже идеально подписанный подменный сертификат будет отклонён, так как его отпечаток не совпадает с зафиксированным в приложении.

Термин pinning происходит от pin — «штырь» или «фиксатор»: разработчик фиксирует доверенный сертификат, и любое отклонение от него блокирует соединение. По данным исследования Mitre CWE-295, improper certificate validation остаётся одной из top-10 наиболее опасных ошибок безопасности в мобильных приложениях, и Certificate Pinning является прямым методом её предотвращения.

История и эволюция Certificate Pinning

Изначально Certificate Pinning использовался в браузерах через механизм HPKP (HTTP Public Key Pinning), стандартизированный в RFC 7469. Разработчик отправлял HTTP-заголовок Public-Key-Pins с хешами ожидаемых ключей, и браузер запоминал их на указанный срок. Однако HPKP оказался опасным: одна ошибка в конфигурации могла заблокировать сайт на месяцы. В 2018 году Chrome прекратил поддержку HPKP, и сейчас стандартом стала программная реализация на стороне клиента — внутри мобильного приложения или браузерного расширения.

Как работает Certificate Pinning?

Процесс Certificate Pinning включает три ключевых этапа: вычисление отпечатка, проверка при соединении и обработка ошибки. На этапе подготовки разработчик получает SHA-256 хеш сертификата или открытого ключа продакшен-сервера. Для GDPR- и PCI DSS-совместимых приложений также требуется зафиксировать отпечатки промежуточных CA в цепочке.

При каждом HTTPS-запросе приложение перехватывает callback аутентификации TLS, извлекает сертификат сервера и вычисляет его SHA-256 хеш. Этот хеш сравнивается с сохранённым списком доверенных отпечатков. Если совпадение найдено — соединение продолжается. Если нет — приложение должно разорвать соединение и сообщить об ошибке, не раскрывая деталей реализации злоумышленнику.

kotlin
fun validateCertificate(certificate: X509Certificate,
    expectedHash: String): Boolean {
    val digest = MessageDigest.getInstance("SHA-256")
    val hash = Base64.encodeToString(
        digest.digest(certificate.publicKey.getEncoded()),
        Base64.DEFAULT
    ).trim()
    return hash == expectedHash
}

Функция принимает объект X509Certificate от сервера и ожидаемый хеш. Сначала извлекается открытый ключ сертификата, вычисляется SHA-256 хеш и кодируется в Base64. Результат сравнивается с ожидаемым отпечатком. В продакшене стоит добавить проверку по массиву из 2–3 отпечатков для поддержки ротации.

Certificate Pinning vs Public Key Pinning

При реализации pinning нужно выбрать, какой именно криптографический объект фиксировать. Certificate Pinning привязывается к самому сертификату X.509 — его серийному номеру, сроку действия и всей цепочке. Public Key Pinning фиксирует только открытый ключ внутри сертификата, игнорируя остальные поля. Выбор существенно влияет на операционные расходы.

КритерийCertificate PinningPublic Key Pinning
Объект фиксацииСертификат X.509 целикомОткрытый ключ RSA/ECDSA
РотацияТребует обновления при каждом перевыпускеНе меняется при смене сертификата с тем же ключом
БезопасностьМаксимально точная привязкаМенее чувствителен к деталям
ГибкостьНизкая — сертификаты меняются каждый 1–2 годаВысокая — ключи могут жить 5–10 лет
РекомендацияДля критичных систем с контролируемыми обновлениямиДля большинства мобильных приложений и API

Public Key Pinning — предпочтительный выбор для большинства проектов. Открытые ключи серверов обычно остаются неизменными при перевыпуске сертификата — компания просто подписывает старый ключ новым сертификатом. Это означает, что приложение не требует обновления после смены сертификата, если ключевая пара не менялась. Certificate Pinning же рекомендован для сценариев, где разработчик полностью контролирует и сервер, и клиентский код, например, в корпоративных приложениях с жёстким циклом обновления.

Trust On First Use (TOFU)

TOFU — стратегия, при которой Certificate Pinning не настраивается заранее, а запоминает сертификат при первом соединении с сервером. Этот подход удобен для приложений, которые не знают заранее, к какому серверу будут подключаться. Недостаток — уязвимость при первой атаке: если первое соединение перехвачено, подменный сертификат будет принят как доверенный. TOFU применяется в SSH-соединениях и некоторых P2P-протоколах.

Реализация на iOS и Android

На обеих платформах Certificate Pinning реализуется через перехват TLS-соединения на уровне сетевого стека. На iOS используется делегат URLSession или Alamofire ServerTrustManager. На Android предпочтительный способ — OkHttp CertificatePinner, который встроен в популярные HTTP-клиенты и поддерживает конфигурацию нескольких отпечатков для каждого домена.

swift
func validate(serverTrust: SecTrust,
    pinnedHash: String) -> Bool {
    guard let certificates = SecTrustCopyCertificateChain(serverTrust)
        as? [SecCertificate] else { return false }

    for certificate in certificates {
        let data = SecCertificateCopyData(certificate)
        var hash = Data(repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
        data.withUnsafeBytes {
            CC_SHA256($0.baseAddress,
                CC_LONG(data.count), &hash)
        }
        if hash.base64EncodedString() == pinnedHash {
            return true
        }
    }
    return false
}

В Swift-функции из serverTrust извлекается цепочка сертификатов, для каждого вычисляется SHA-256 хеш, и результат сравнивается с ожидаемым. Проход по всем сертификатам в цепочке позволяет реализовать pinning на уровне промежуточного CA — если промежуточный сертификат совпадает, соединение принимается. Это даёт гибкость при ротации leaf-сертификатов.

TrustManager для Android (кастомный)

Если приложение не использует OkHttp, Certificate Pinning можно реализовать через кастомный X509TrustManager. Этот метод требует больше кода, но даёт полный контроль над процессом проверки. TrustManager переопределяет метод checkServerTrusted, где разработчик вручную проверяет сертификаты сервера и принимает решение о доверии. Рекомендуется только для специфических сценариев, где библиотека OkHttp недоступна.

Ошибки при внедрении Certificate Pinning

Самая частая ошибка — отсутствие backup pins. Разработчик закладывает один отпечаток сертификата, и при его истечении пользователи массово теряют соединение. Минимально допустимая конфигурация — два отпечатка: текущий сертификат и резервный. Оптимально — три: текущий, резервный и отпечаток корневого CA как fallback.

Вторая ошибка — хранение pins в открытом виде в коде. Атакующий с доступом к APK или IPA может легко извлечь отпечатки и подменить их. Рекомендуется обфускация хешей: разбить строку на части, хранить в ресурсах с шифрованием или вычислять через runtime. Для Android эффективен ProGuard с обфускацией строковых констант.

Третья ошибка — пиннинг на уровне development-сертификата. Сертификаты разработки и продакшена обычно разные, но разработчики часто забывают переключить pins при сборке релиза. Результат — продакшен-приложение не может соединиться с сервером. Решение — раздельная конфигурация pins для debug и release через BuildConfig или flavour-специфичные ресурсы.

  • Ignoring certificate chain — проверка только leaf-сертификата без учёта промежуточных CA, что ломает соединение при ротации
  • Hardcoded dates — жёстко зашитые даты истечения сертификатов, которые не меняются после обновления
  • No monitoring — отсутствие алертов на ошибки Certificate Pinning, из-за чего проблемы обнаруживаются только от пользователей
  • TOFU без валидации — использование Trust On First Use без дополнительной проверки, позволяющее первой MITM-атаке зафиксировать подменный сертификат

Часто задаваемые вопросы

Чем Certificate Pinning отличается от SSL Pinning?

SSL Pinning — общий термин для привязки к SSL/TLS сертификату. Certificate Pinning — конкретная реализация, фиксирующая сам сертификат X.509, а не только открытый ключ. Разница в объекте привязки: сертификат vs ключ.

Как безопасно хранить отпечатки сертификатов в приложении?

Рекомендуется хранить хеши в ресурсах с обфускацией через ProGuard (Android) или зашифрованными через Keychain (iOS). Избегайте хранения pins в открытом виде в strings.xml или Info.plist без шифрования.

Как часто нужно менять pinned отпечатки?

При каждой смене сертификата на сервере. Рекомендуется добавлять новый отпечаток как backup pin за 3–6 месяцев до истечения текущего, а после ротации удалять старый. Минимум один backup pin обязателен.

Можно ли отключить Certificate Pinning для отладки?

Да, через условную компиляцию: в debug-сборке pinning отключён, в release — включён. Используйте BuildConfig.DEBUG на Android или #if DEBUG на iOS для переключения. Никогда не делайте это через runtime-флаг, доступный пользователю.

Что делать если сертификат скомпрометирован?

Немедленно выпустить обновление приложения с новыми отпечатками и опубликовать его в сторах. Использовать механизм форсированного обновления. Если backup pins включали отпечаток резервного CA, временно можно переключиться на другой домен с другим сертификатом.

Итоги

  • Certificate Pinning — фиксация доверенного сертификата или его открытого ключа для защиты от MITM-атак через подменные CA
  • Два подхода — certificate pinning (строгий, к сертификату) и public key pinning (гибкий, к открытому ключу)
  • Backup pins обязательны — минимум 2 отпечатка для обеспечения непрерывности при ротации сертификатов
  • OkHttp CertificatePinner — стандартный способ реализации на Android с поддержкой нескольких pins
  • URLSessionDelegate — основной способ на iOS с ручной проверкой SecTrust и SHA-256 хешей
  • Ошибки — отсутствие backup pins, хранение без обфускации, путаница debug/release конфигураций
  • Рекомендация — использовать public key pinning для большинства проектов и Certificate Pinning только для критичных систем

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также