Illustration for Certificate Pinning in Mobile Apps

Certificate pinning is a security technique used to prevent attackers from intercepting and manipulating communication between two resources.

When an app communicates with a server over HTTPS, it verifies the server's SSL/TLS certificate against a list of trusted certificate authorities (CAs). However, this traditional approach is vulnerable to attacks where adversaries can impersonate legitimate servers by presenting fraudulent certificates signed by rogue CAs (man-in-the-middle attacks).

Illustration for Certificate Pinning in Mobile Apps

Certificate pinning mitigates this risk by associating a specific SSL/TLS certificate or public key with a particular domain:

Types of Attacks Prevented

Certificate pinning helps mitigate several security threats, including:

Illustration for Certificate Pinning in Mobile Apps

Swift Example

Let's look at an example using Alamofire, a true blast from the past!

import Alamofire
import Security

class NetworkManager {
    
    static let sharedInstance = NetworkManager()
    
    private let pinnedCertificates: [SecCertificate] = [
        // Add your pinned certificates here
        loadCertificate(filename: "example_cert")
    ]
    
    private static func loadCertificate(filename: String) -> SecCertificate {
        let filePath = Bundle.main.path(forResource: filename, ofType: "der")!
        let fileData = try! Data(contentsOf: URL(fileURLWithPath: filePath))
        let certificate = SecCertificateCreateWithData(nil, fileData as CFData)!
        return certificate
    }
    
    private func evaluateServerTrust(trust: SecTrust) -> Bool {
        let policy = SecPolicyCreateSSL(true, nil)
        SecTrustSetPolicies(trust, policy)
        
        SecTrustSetAnchorCertificates(trust, pinnedCertificates as CFArray)
        SecTrustSetAnchorCertificatesOnly(trust, true)
        
        var result: SecTrustResultType = .invalid
        SecTrustEvaluate(trust, &result)
        
        return result == .unspecified || result == .proceed
    }
    
    func makeRequest(url: URL) {
        Alamofire.request(url).validate { request, response, data in
            guard let serverTrust = response.serverTrust else { return .failure(AFError.serverTrustEvaluationFailed(reason: .noCertificatesFound)) }
            let isServerTrusted = self.evaluateServerTrust(trust: serverTrust)
            return .success(())
        }.response { response in
            // Handle response
        }
    }
}

How It Works

Final Thoughts

Take your users' security seriously and implement certificate pinning. It's easy to do, and it's a no-brainer, especially if you are handling sensitive data.