Certificate Pinning — механизъм за фиксиране на сертификата или публичния ключ на сървъра, при който приложението използва предварително известен отпечатък за проверка на HTTPS връзката. За разлика от стандартната верига на доверие чрез CA, pinning гарантира, че дори компрометиран сертификационен орган не може да издаде фалшив сертификат за вашия домейн. Според OWASP MSTG (2025), Certificate Pinning е в списъка на задължителните контроли за приложения с ниво на защита L2. Имплементацията включва съхраняване на хешове на сертификати в кода и проверка при всяка заявка.
Основни точки
Certificate Pinning — техника за сигурност, при която приложението съхранява отпечатъка (fingerprint) на доверен сертификат и го използва като единствен критерий за установяване на HTTPS връзка. В стандартния TLS модел клиентът проверява дали сертификатът на сървъра е подписан от доверен коренов CA — който и да е от стотиците предварително инсталирани органи в системата. Certificate Pinning заменя тази верига с директна проверка: сертификатът трябва да съвпада със съхранения образец или да съдържа очаквания публичен ключ.
Проблемът на стандартния модел стана очевиден след инцидентите с компрометиране на CA — DigiNotar (2011), Comodo (2011), TrustCor (2022). Ако CA издаде фалшив сертификат за вашия домейн, браузърът или приложението го приема като валиден. Certificate Pinning предотвратява тази атака: дори перфектно подписан фалшив сертификат ще бъде отхвърлен, тъй като отпечатъкът му не съвпада с фиксирания в приложението.
Терминът pinning произлиза от pin — „щифт“ или „фиксатор“: разработчикът фиксира доверения сертификат и всяко отклонение от него блокира връзката. Според изследване на Mitre CWE-295, неправилната валидация на сертификати остава една от топ-10 най-опасните грешки в сигурността на мобилни приложения, а Certificate Pinning е директният метод за предотвратяването ѝ.
Първоначално Certificate Pinning се използваше в браузъри чрез механизма HPKP (HTTP Public Key Pinning), стандартизиран в RFC 7469. Разработчикът изпращаше HTTP заглавка Public-Key-Pins с хешове на очаквани ключове, а браузърът ги запомняше за определен период. HPKP обаче се оказа опасен: една грешка в конфигурацията можеше да блокира сайта за месеци. През 2018 г. Chrome прекрати поддръжката на HPKP и сега стандартът е програмна имплементация от страна на клиента — вътре в мобилното приложение или разширението на браузъра.
Процесът на Certificate Pinning включва три ключови етапа: изчисляване на отпечатъка, проверка при свързване и обработка на грешка. В етапа на подготовка разработчикът получава SHA-256 хеш на сертификата или публичния ключ на продукционния сървър. За приложения, съвместими с GDPR и PCI DSS, се изисква също фиксиране на отпечатъците на междинните CA във веригата.
При всяка HTTPS заявка приложението прихваща callback за удостоверяване TLS, извлича сертификата на сървъра и изчислява неговия SHA-256 хеш. Този хеш се сравнява със съхранения списък от доверени отпечатъци. Ако се намери съвпадение — връзката продължава. Ако не — приложението трябва да прекъсне връзката и да отчете грешката, без да разкрива детайли по имплементацията на атакуващия.
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 отпечатъка за поддръжка на ротация.
При имплементация на pinning трябва да се избере кой криптографски обект да се фиксира. Certificate Pinning се свързва със самия X.509 сертификат — неговия сериен номер, период на валидност и цялата верига. Public Key Pinning фиксира само публичния ключ вътре в сертификата, игнорирайки останалите полета. Изборът значително влияе върху оперативните разходи.
| Критерий | Certificate Pinning | Public Key Pinning |
|---|---|---|
| Обект на фиксиране | X.509 сертификат изцяло | Публичен ключ RSA/ECDSA |
| Ротация | Изисква актуализация при всяко преиздаване | Не се променя при смяна на сертификата със същия ключ |
| Сигурност | Максимално прецизно свързване | По-малко чувствителен към детайли |
| Гъвкавост | Ниска — сертификатите се сменят на 1–2 години | Висока — ключовете могат да живеят 5–10 години |
| Препоръка | За критични системи с контролирани актуализации | За повечето мобилни приложения и API |
Public Key Pinning — предпочитан избор за повечето проекти. Публичните ключове на сървърите обикновено остават непроменени при преиздаване на сертификата — компанията просто подписва стария ключ с нов сертификат. Това означава, че приложението не изисква актуализация след смяна на сертификата, ако двойката ключове не е променена. Certificate Pinning се препоръчва за сценарии, при които разработчикът контролира напълно както сървъра, така и клиентския код, например в корпоративни приложения със строг цикъл на актуализация.
TOFU — стратегия, при която Certificate Pinning не е конфигуриран предварително, а запомня сертификата при първата връзка със сървъра. Този подход е удобен за приложения, които не знаят предварително към кой сървър ще се свържат. Недостатък — уязвимост при първата атака: ако първата връзка бъде прихваната, фалшивият сертификат ще бъде приет за доверен. TOFU се прилага в SSH връзки и някои P2P протоколи.
И на двете платформи Certificate Pinning се имплементира чрез прихващане на TLS връзката на ниво мрежов стек. На iOS се използва делегат на URLSession или Alamofire ServerTrustManager. На Android предпочитаният начин е OkHttp CertificatePinner, който е вграден в популярни HTTP клиенти и поддържа конфигурация на множество отпечатъци за всеки домейн.
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 сертификати.
Ако приложението не използва OkHttp, Certificate Pinning може да се имплементира чрез персонализиран X509TrustManager. Този метод изисква повече код, но дава пълен контрол върху процеса на проверка. TrustManager презаписва метода checkServerTrusted, където разработчикът ръчно проверява сертификатите на сървъра и взема решение за доверие. Препоръчва се само за специфични сценарии, при които библиотеката OkHttp не е налична.
Най-честата грешка — липса на резервни пинове. Разработчикът поставя един отпечатък на сертификата и при неговото изтичане потребителите масово губят връзка. Минимално приемливата конфигурация — два отпечатъка: текущ сертификат и резервен. Оптимално — три: текущ, резервен и отпечатък на коренов CA като fallback.
Втора грешка — съхраняване на пинове в отворен вид в кода. Атакуващ с достъп до APK или IPA може лесно да извлече и замени отпечатъците. Препоръчва се объркване на хешове: разделяне на низа на части, съхраняване в криптирани ресурси или изчисляване чрез runtime. За Android е ефективен ProGuard с объркване на низови константи.
Трета грешка — pinning на ниво development сертификат. Разработчиците и продукционните сертификати обикновено са различни, но разработчиците често забравят да сменят пиновете при изграждане на release. Резултат — продукционното приложение не може да се свърже със сървъра. Решение — отделна конфигурация на пинове за debug и release чрез BuildConfig или специфични за flavour ресурси.
Често задавани въпроси
SSL Pinning — общ термин за свързване към SSL/TLS сертификат. Certificate Pinning — конкретна имплементация, която фиксира самия X.509 сертификат, а не само публичния ключ. Разликата е в обекта на фиксиране: сертификат спрямо ключ.
Препоръчва се съхраняване на хешове в ресурси с объркване чрез ProGuard (Android) или криптирани чрез Keychain (iOS). Избягвайте съхраняване на пинове в отворен вид в strings.xml или Info.plist без криптиране.
При всяка смяна на сертификата на сървъра. Препоръчва се добавяне на нов отпечатък като резервен pin 3–6 месеца преди изтичане на текущия, а след ротация премахване на стария. Минимум един резервен pin е задължителен.
Да, чрез условна компилация: в debug сборката pinning е изключен, в release — включен. Използвайте BuildConfig.DEBUG на Android или #if DEBUG на iOS за превключване. Никога не правете това чрез runtime флаг, достъпен за потребителя.
Незабавно пуснете актуализация на приложението с нови отпечатъци и я публикувайте в магазините. Използвайте механизъм за принудителна актуализация. Ако резервните пинове включват отпечатък на резервен CA, временно можете да преминете към друг домейн с друг сертификат.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също