SSL Pinning — technika bezpieczeństwa, w której aplikacja weryfikuje certyfikat serwera na podstawie wcześniej znanego odcisku lub certyfikatu, zamiast polegać na łańcuchu zaufania CA. W przeciwieństwie do standardowej weryfikacji, pinning zapobiega przechwytywaniu ruchu przez podstawione główne centra certyfikacji. Według OWASP Mobile Security Testing Guide (2025), technika ta znajduje się w top-3 recommended controls do ochrony przed atakami MITM. Bez pinning atakujący z podstawionym głównym certyfikatem może odszyfrować cały ruch HTTPS aplikacji.
Najważniejsze
SSL Pinning — to mechanizm bezpieczeństwa, w którym aplikacja mobilna lub webowa zapamiętuje zaufany certyfikat lub klucz publiczny serwera i odrzuca wszelkie połączenia, których certyfikat nie pasuje do zapisanego. W standardowym schemacie HTTPS klient weryfikuje certyfikat poprzez łańcuch zaufania do głównego CA — każdy CA może podpisać certyfikat dla dowolnej domeny. SSL Pinning eliminuje tę słabość: zamiast ufać setkom CA, aplikacja ufa tylko jednemu konkretnemu certyfikatowi.
Problem standardowej weryfikacji polega na tym, że każdy z setek głównych CA może wydać ważny certyfikat dla Twojej domeny — przypadkowo lub pod przymusem. Atakujący, który uzyska dostęp do korporacyjnego proxy z własnym głównym certyfikatem, może przeprowadzić atak MITM bez ostrzeżenia przeglądarki. SSL Pinning zamyka tę lukę: nawet jeśli CA wyda podstawiony certyfikat, aplikacja go odrzuci, ponieważ odcisk nie pasuje do zapisanego.
W aplikacjach mobilnych SSL Pinning jest szczególnie ważny, ponieważ urządzenia często działają w niezabezpieczonych sieciach — publiczne Wi-Fi, korporacyjne proxy z inspekcją ruchu, zainfekowane punkty dostępu. Według Verizon Mobile Security Index (2025), ponad 60% wycieków danych w aplikacjach mobilnych jest związanych z przechwytywaniem ruchu na poziomie transportowym.
Aplikacje mobilne przesyłają wrażliwe dane — tokeny uwierzytelniania, informacje płatnicze, dane osobowe użytkowników. Bez dodatkowej ochrony HTTPS może być skompromitowany poprzez podstawienie głównego certyfikatu na urządzeniu — na przykład po instalacji profilu korporacyjnego lub złośliwej aplikacji. SSL Pinning gwarantuje, że nawet jeśli na urządzeniu jest zainstalowany podstawiony główny CA, aplikacja będzie nadal weryfikować certyfikat według własnej białej listy.
Proces SSL Pinning składa się z trzech etapów: przechwycenie odcisku, weryfikacja przy połączeniu i obsługa błędu. Na etapie rozwoju inżynier pobiera odcisk SHA-256 certyfikatu serwera (openssl x509 -fingerprint -sha256) i osadza go w kodzie aplikacji lub pliku konfiguracyjnym. Przy każdym żądaniu HTTPS aplikacja oblicza odcisk otrzymanego certyfikatu i porównuje go z zapisanym — jeśli wartości nie są zgodne, połączenie jest zrywane.
Pierwszy etap — pinning na etapie budowania: programista zna z góry certyfikaty serwera i osadza ich hasze. Drugi etap — pinning przy pierwszym połączeniu (trust on first use, TOFU): aplikacja zapamiętuje certyfikat przy pierwszym żądaniu i używa go do weryfikacji wszystkich kolejnych. TOFU jest wygodny dla środowisk dynamicznych, ale podatny na atak przy pierwszym połączeniu — jeśli pierwsze połączenie jest już przechwycone, podstawiony certyfikat zostanie zaakceptowany jako zaufany.
Krytyczny szczegół — zapasowe odciski (backup pins). Certyfikaty mają okres ważności, a przy ich wymianie aplikacja bez aktualizacji straci połączenie z serwerem. Inżynierowie dodają 2–3 dodatkowe odciski — na przykład odcisk zapasowego certyfikatu i odcisk głównego CA. Jeśli główny certyfikat się zmienia, aplikacja sprawdza według backup pins i połączenie nadal działa.
# Uzyskanie odcisku SHA-256 certyfikatu
openssl s_client -connect example.com:443 </dev/null 2>/dev/null | \
openssl x509 -pubkey -noout | \
openssl pkey -pubin -outform der | \
openssl dgst -sha256 -binary | \
base64
Istnieją dwa główne podejścia do implementacji pinning: przywiązanie do całego certyfikatu (certificate pinning) i przywiązanie do klucza publicznego (public key pinning). Każde podejście ma swoje mocne strony i ograniczenia, które wpływają na bezpieczeństwo i łatwość utrzymania.
| Typ | Obiekt fiksacji | Elastyczność | Bezpieczeństwo |
|---|---|---|---|
| Certificate Pinning | Cały certyfikat X.509 | Niska — przy zmianie certyfikatu wymagana aktualizacja | Wysokie — dokładne przywiązanie |
| Public Key Pinning | Klucz publiczny certyfikatu | Średnia — klucz może być w nowym certyfikacie | Wysokie — mniej wrażliwy na szczegóły certyfikatu |
| Hash Pinning | Hash SHA-256 certyfikatu lub klucza | Wysoka — można zmieniać certyfikaty bez zmiany klucza | Średnie — zależy od odporności hasha |
Przywiązanie do certyfikatu — najściślejsza metoda. Aplikacja przechowuje kopię zaufanego certyfikatu lub jego odcisk SHA-256 i porównuje z certyfikatem serwera przy każdym połączeniu HTTPS. Ta metoda zapewnia maksymalne bezpieczeństwo, ale stwarza problemy przy rotacji — certyfikaty zwykle działają 1–2 lata, po czym wymagana jest wymuszona aktualizacja aplikacji. Zalecana dla krytycznych systemów z kontrolowanym cyklem aktualizacji.
Fiksacja klucza publicznego — bardziej elastyczne podejście. Zamiast całego certyfikatu aplikacja zapamiętuje tylko klucz publiczny RSA lub ECDSA serwera. Klucz może pozostać niezmieniony przy ponownym wydaniu certyfikatu, jeśli firma używa tej samej pary kluczy. Zmniejsza to częstotliwość aktualizacji aplikacji. Jednak jeśli klucz zostanie skompromitowany, wymagana będzie kaskadowa wymiana u wszystkich klientów.
Na platformie Apple SSL Pinning jest implementowany przez delegata URLSession. Programista tworzy klasę implementującą protokół URLSessionDelegate i nadpisuje metodę didReceive challenge, w której ręcznie weryfikuje certyfikat serwera względem zapisanych odcisków. Alternatywne podejście — użycie Alamofire z ServerTrustManager, który upraszcza konfigurację.
class SSLPinningDelegate: NSObject, URLSessionDelegate {
let pinnedHash = "sha256/Wi24BE7j5qLk0iLvPq6ePEsVRqZ1yW0F6wLg="
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard let serverTrust = challenge.protectionSpace.serverTrust
else { return completionHandler(.cancelAuthenticationChallenge, nil) }
if validate(serverTrust, pinnedHash) {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}
W przykładzie delegat otrzymuje żądanie uwierzytelnienia od URLSession, wyodrębnia serverTrust z challenge i porównuje odcisk SHA-256 certyfikatu z zapisanym. Jeśli odcisk się zgadza — połączenie jest kontynuowane, w przeciwnym razie challenge jest odrzucane. Dla produkcji warto dodać weryfikację kilku backup pins i logowanie błędów do monitorowania.
Od iOS 14 Apple dodało wbudowane wsparcie dla Certificate Pinning przez Info.plist. Programista wskazuje zaufane certyfikaty w kluczu NSAppTransportSecurity z pod słownikiem NSPinnedDomains. To podejście nie wymaga pisania kodu, ale jest mniej elastyczne — nie można dynamicznie zmieniać pins ani logować błędów weryfikacji.
Na Android istnieją trzy główne sposoby implementacji SSL Pinning: przez CertificatePinner biblioteki OkHttp, przez Network Security Config w XML i przez niestandardową weryfikację w HttpsURLConnection. OkHttp — najpopularniejsze i zalecane podejście, używane w Retrofit i innych klientach HTTP.
val certificatePinner = CertificatePinner.Builder()
.add("api.example.com",
"sha256/Wi24BE7j5qLk0iLvPq6ePEsVRqZ1yW0F6wLg=")
.add("api.example.com",
"sha256/FiPq6ePEsVRqZ1yW0F6wLgWi24BE7j5qLk0iL=") // zapasowy pin
.build()
val client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build()
W konfiguracji OkHttp programista wskazuje domenę i jeden lub kilka odcisków SHA-256. Przy pierwszym odcisku OkHttp porównuje certyfikat serwera z określonymi pins. Jeśli nie ma zgodności, klient zgłasza SSLPeerUnverifiedException. Backup pin jest obowiązkowy — bez niego przy zmianie certyfikatu zapytania API natychmiast zaczną padać.
Android obsługuje deklaratywny Certificate Pinning przez konfigurację XML od API 24. Plik res/xml/network_security_config.xml zawiera listę domen i ich odcisków. Ta metoda jest wygodna dla statycznych konfiguracji, ale nie pozwala na implementację TOFU ani niestandardowej logiki weryfikacji z logowaniem anomalii.
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2027-12-31">
<pin digest="SHA-256">
Wi24BE7j5qLk0iLvPq6ePEsVRqZ1yW0F6wLg=</pin>
<pin digest="SHA-256">
FiPq6ePEsVRqZ1yW0F6wLgWi24BE7j5qLk0iL=</pin>
</pin-set>
</domain-config>
</network-security-config>
SSL Pinning znacznie zwiększa bezpieczeństwo aplikacji mobilnej, ale wprowadza złożoność operacyjną. Główna zaleta — ochrona przed atakami MITM nawet przy kompromitacji głównych CA. Aplikacja ufa tylko tym certyfikatom, które są jawnie wskazane przez programistę, a nie całej infrastrukturze publicznych centrów certyfikacji. Jest to szczególnie krytyczne dla aplikacji finansowych, komunikatorów i aplikacji z wrażliwymi danymi.
Główna wada — złożoność rotacji certyfikatów. Jeśli certyfikat wygasa lub zostaje unieważniony, użytkownicy bez aktualizacji aplikacji tracą połączenie. Rozwiązuje się to przez backup pins i mechanizm stopniowej aktualizacji: nowa aplikacja zna stary i nowy certyfikat, a po pełnej aktualizacji użytkowników stary pin jest usuwany z kodu. Zaleca się dodanie minimum 2 backup pins — jeden dla bieżącego certyfikatu, jeden dla przyszłego.
Kolejny kompromis — niemożność używania publicznych proxy do debugowania ruchu (Charles Proxy, Burp Suite) bez wyłączania pinning. Utrudnia to debugowanie zapytań sieciowych na etapie programowania. Rozwiązanie — kompilacja warunkowa: w kompilacji debug pinning jest wyłączony, w release — włączony. OWASP zaleca używanie flagi BuildConfig.DEBUG do przełączania.
| Aspekt | Zaleta | Wada |
|---|---|---|
| Bezpieczeństwo | Ochrona przed MITM przez podstawione CA | Złożoność przy kompromitacji klucza |
| Utrzymanie | Jawna kontrola zaufania | Rotacja wymaga aktualizacji aplikacji |
| Debugowanie | Gwarancja połączenia z właściwym serwerem | Blokowanie proxy debugowania |
Często zadawane pytania
Standardowa weryfikacja HTTPS ufa każdemu certyfikatowi podpisanemu przez znany główny CA. SSL Pinning ufa tylko konkretnemu certyfikatowi lub kluczowi — jeśli CA wyda podstawiony certyfikat, aplikacja go odrzuci.
Certyfikaty zwykle działają 1–2 lata. Zaleca się aktualizację pins na 3–6 miesięcy przed wygaśnięciem bieżącego certyfikatu, dodając nowy odcisk jako backup pin, a po rotacji usunąć stary.
Tak, ale należy uwzględnić, że CDN może zmieniać certyfikaty przy przełączaniu między serwerami brzegowymi. Zaleca się przywiązywanie do klucza publicznego, a nie do konkretnego certyfikatu, i używanie kilku backup pins.
Połączenie jest zrywane z błędem — na Android jest to SSLPeerUnverifiedException, na iOS challenge jest odrzucane z .cancelAuthenticationChallenge. Aplikacja powinna poprawnie obsłużyć ten błąd i powiadomić użytkownika.
Nie, ale OWASP zaleca go dla aplikacji pracujących z wrażliwymi danymi: bankowość, medycyna, systemy korporacyjne. Dla prostych aplikacji read-only standardowa weryfikacja HTTPS z certyfikatami EV jest zwykle wystarczająca.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również