Why Certificate Pinning Matters
Certificate pinning prevents man-in-the-middle attacks by verifying that your app only communicates with servers presenting specific, known certificates.
Without Pinning
Attackers can:
- Intercept all network traffic
- Steal credentials and payment data
- Modify transactions in transit
- Inject malicious responses
PCI DSS Requirement
PCI DSS 4.2.1 requires:
Strong cryptography is used to safeguard sensitive data during transmission
Certificate pinning is a critical control for meeting this requirement.
Implementation Guide
Android (OkHttp)
val certificatePinner = CertificatePinner.Builder()
.add("api.yourapp.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.add("api.yourapp.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")
.build()
val client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build()
iOS (URLSession)
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard let serverTrust = challenge.protectionSpace.serverTrust,
let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
// Verify certificate matches pinned hash
let serverCertData = SecCertificateCopyData(certificate) as Data
if pinnedCertificates.contains(serverCertData.sha256Hash) {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
Best Practices
1. Pin Multiple Certificates
Include backup pins:
- Current certificate
- Backup certificate
- Root CA (optional fallback)
2. Plan for Rotation
Certificates expire. Have a plan:
- Include next certificate before expiry
- Monitor certificate expiration
- Force update mechanism
3. Handle Failures Gracefully
When pinning fails:
- Block the connection
- Log the attempt
- Alert security team
- Don't fall back to unpinned
4. Test Thoroughly
Verify pinning works:
- Use proxy tools (Charles, mitmproxy)
- Confirm connections are rejected
- Test in production-like environment
Verify your certificate pinning implementation. Scan your app.