Certificate Pinning — en mekanism för att fixera serverns certifikat eller offentliga nyckel, där appen använder ett för känt avtryck för att verifiera HTTPS-anslutningen. Till skillnad från den standardmässiga förtroendekedjan via CA garanterar pinning att även en komprometterad certifikatutfärdare inte kan utfärda ett falskt certifikat för din domän. Enligt OWASP MSTG (2025) finns Certificate Pinning med på listan över obligatoriska kontroller för appar med skyddsnivå L2. Implementeringen innefattar lagring av certifikathashar i koden och verifiering vid varje begäran.
Huvudpunkter
Certificate Pinning — är en säkerhetsteknik där appen lagrar avtrycket (fingerprint) av ett betrott certifikat och använder det som det enda kriteriet för att upprätta en HTTPS-anslutning. I standard TLS-modellen kontrollerar klienten om serverns certifikat är signerat av en betrodd root-CA — någon av hundratals förinstallerade utfärdare i systemet. Certificate Pinning ersätter denna kedja med en direkt kontroll: certifikatet måste matcha det lagrade provet eller innehålla den förväntade offentliga nyckeln.
Problemet med standardmodellen blev uppenbart efter incidenter med CA-kompromettering — DigiNotar (2011), Comodo (2011), TrustCor (2022). Om en CA utfärdar ett falskt certifikat för din domän, accepterar webbläsaren eller appen det som giltigt. Certificate Pinning förhindrar denna attack: även ett perfekt signerat falskt certifikat kommer att avvisas, eftersom dess avtryck inte matchar det som fixerats i appen.
Termen pinning kommer från pin — ”nål” eller ”fixerare”: utvecklaren fixerar det betrodda certifikatet, och varje avvikelse från det blockerar anslutningen. Enligt forskning från Mitre CWE-295 förblir felaktig certifikatvalidering ett av de 10 farligaste säkerhetsfelen i mobilappar, och Certificate Pinning är den direkta metoden för att förebygga det.
Ursprungligen användes Certificate Pinning i webbläsare genom HPKP-mekanismen (HTTP Public Key Pinning), standardiserad i RFC 7469. Utvecklaren skickade en HTTP-rubrik Public-Key-Pins med hash av förväntade nycklar, och webbläsaren kom ihåg dem under en angiven period. HPKP visade sig dock vara farligt: ett enda konfigurationsfel kunde blockera webbplatsen i månader. 2018 slutade Chrome att stödja HPKP och nu är standarden mjukvaruimplementering på klientsidan — inuti mobilappen eller webbläsartillägget.
Certificate Pinning-processen omfattar tre nyckelsteg: beräkning av avtryck, verifiering vid anslutning och felhantering. I förberedelsesteget får utvecklaren SHA-256-hashen av produktionsserverns certifikat eller offentliga nyckel. För GDPR- och PCI DSS-kompatibla appar krävs även fixering av avtryck från mellanliggande CA i kedjan.
Vid varje HTTPS-begäran fångar appen TLS-autentiseringscallback, extraherar serverns certifikat och beräknar dess SHA-256-hash. Denna hash jämförs med den lagrade listan över betrodda avtryck. Om en överensstämmelse hittas — fortsätter anslutningen. Om inte — måste appen avbryta anslutningen och rapportera felet, utan att avslöja implementeringsdetaljer för angriparen.
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
}
Funktionen tar emot ett X509Certificate-objekt från servern och den förväntade hashen. Först extraheras certifikatets offentliga nyckel, SHA-256-hashen beräknas och kodas i Base64. Resultatet jämförs med det förväntade avtrycket. I produktion är det värt att lägga till kontroll på en array av 2–3 avtryck för att stödja rotation.
Vid implementering av pinning måste man välja vilket kryptografiskt objekt som ska fixeras. Certificate Pinning binder till själva X.509-certifikatet — dess serienummer, giltighetsperiod och hela kedjan. Public Key Pinning fixerar endast den offentliga nyckeln inuti certifikatet och ignorerar övriga fält. Valet påverkar driftskostnaderna avsevärt.
| Kriterium | Certificate Pinning | Public Key Pinning |
|---|---|---|
| Fixeringsobjekt | X.509-certifikat i sin helhet | Offentlig nyckel RSA/ECDSA |
| Rotation | Kräver uppdatering vid varje nyutfärdande | Ändras inte vid certifikatbyte med samma nyckel |
| Säkerhet | Maximalt precis bindning | Mindre känslig för detaljer |
| Flexibilitet | Låg — certifikat byts var 1–2 år | Hög — nycklar kan leva 5–10 år |
| Rekommendation | För kritiska system med kontrollerade uppdateringar | För de flesta mobilappar och API:er |
Public Key Pinning — föredraget val för de flesta projekt. Servrarnas offentliga nycklar förblir vanligtvis oförändrade vid nyutfärdande av certifikatet — företaget signerar helt enkelt den gamla nyckeln med ett nytt certifikat. Detta innebär att appen inte kräver uppdatering efter certifikatbyte, om nyckelparet inte har ändrats. Certificate Pinning rekommenderas för scenarier där utvecklaren har full kontroll över både servern och klientkoden, till exempel i företagsappar med strikt uppdateringscykel.
TOFU — en strategi där Certificate Pinning inte är förkonfigurerat, utan kommer ihåg certifikatet vid första anslutningen till servern. Detta tillvägagångssätt är bekvämt för appar som inte i förväg vet vilken server de ska ansluta till. Nackdelen — sårbarhet vid första attacken: om den första anslutningen fångas upp kommer det falska certifikatet att accepteras som betrott. TOFU används i SSH-anslutningar och vissa P2P-protokoll.
På båda plattformarna implementeras Certificate Pinning genom att fånga TLS-anslutningen på nätverksstacknivå. På iOS används URLSession-delegat eller Alamofire ServerTrustManager. På Android är den föredragna metoden OkHttp CertificatePinner, som är inbyggd i populära HTTP-klienter och stödjer konfiguration av flera avtryck för varje domän.
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
}
I Swift-funktionen extraheras certifikatkedjan från serverTrust, för varje beräknas SHA-256-hashen och resultatet jämförs med det förväntade. Genom att gå igenom alla certifikat i kedjan kan pinning implementeras på nivån av mellanliggande CA — om det mellanliggande certifikatet matchar, accepteras anslutningen. Detta ger flexibilitet vid rotation av leaf-certifikat.
Om appen inte använder OkHttp kan Certificate Pinning implementeras via en anpassad X509TrustManager. Denna metod kräver mer kod, men ger full kontroll över verifieringsprocessen. TrustManager åsidosätter metoden checkServerTrusted, där utvecklaren manuellt kontrollerar serverns certifikat och fattar ett förtroendebeslut. Rekommenderas endast för specifika scenarier där OkHttp-biblioteket inte är tillgängligt.
Det vanligaste misstaget — avsaknad av reservpins. Utvecklaren placerar ett certifikatavtryck och när det går ut förlorar användare massivt anslutningen. Den minst acceptabla konfigurationen — två avtryck: aktuellt certifikat och reserv. Optimalt — tre: aktuellt, reserv och avtryck av root-CA som fallback.
Andra misstaget — lagring av pins i öppen form i koden. En angripare med tillgång till APK eller IPA kan enkelt extrahera och ersätta avtrycken. Det rekommenderas att obfuskera hash: dela upp strängen i delar, lagra i krypterade resurser eller beräkna via runtime. För Android är ProGuard med obfuskering av strängkonstanter effektivt.
Tredje misstaget — pinning på nivån av utvecklingscertifikat. Utvecklings- och produktionscertifikat är vanligtvis olika, men utvecklare glömmer ofta att byta pins vid bygge av release. Resultatet — produktionsappen kan inte ansluta till servern. Lösning — separat pin-konfiguration för debug och release via BuildConfig eller flavourspecifika resurser.
Vanliga frågor
SSL Pinning — allmän term för bindning till SSL/TLS-certifikat. Certificate Pinning — konkret implementering som fixerar själva X.509-certifikatet, inte bara den offentliga nyckeln. Skillnaden ligger i fixeringsobjektet: certifikat vs nyckel.
Det rekommenderas att lagra hash i resurser med obfuskering via ProGuard (Android) eller krypterade via Keychain (iOS). Undvik att lagra pins i öppen form i strings.xml eller Info.plist utan kryptering.
Vid varje ändring av certifikatet på servern. Det rekommenderas att lägga till ett nytt avtryck som reservpin 3–6 månader före utgången av det aktuella certifikatet, och efter rotation ta bort det gamla. Minst en reservpin är obligatorisk.
Ja, genom villkorlig kompilering: i debug-bygge är pinning inaktiverat, i release — aktiverat. Använd BuildConfig.DEBUG på Android eller #if DEBUG på iOS för växling. Gör aldrig detta via en runtime-flagga som är tillgänglig för användaren.
Släpp omedelbart en app-uppdatering med nya avtryck och publicera den i butikerna. Använd en mekanism för tvångsuppdatering. Om reservpins inkluderade avtrycket av reserv-CA, kan man tillfälligt byta till en annan domän med ett annat certifikat.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också