一種加強 App 和 Server 間通訊安全性的方法。主要目標是確保 App 僅與預先驗證的 Server 建立安全連接,防止中間人攻擊(Man-in-the-Middle,MitM)等安全風險。一般會有兩種方式進行驗證,Certificate Pinning 和 Public Key Pinning
Certificate Pinning 通常需要驗證整張 SSL 證書,因此安全性相對較高。然而,此方法較為固定,如果 Server 更新證書,App 需要定期更新並重新上架。
首先,我們可以建立一個 Certificates
結構,用於從 App 內取得憑證檔案。
struct Certificates {
static let testSSLPinning = Certificates.certificate(filename: "testSSLPinning")
private static func certificate(filename: String, type: String = "cer") -> SecCertificate {
let filePath = Bundle.main.path(forResource: filename, ofType: type)!
let data = try! Data(contentsOf: URL(fileURLWithPath: filePath))
let certificate = SecCertificateCreateWithData(nil, data as CFData)!
return certificate
}
}
建立自定義的Session
allHostsMustBeEvaluated
設置為 false,表示不需要對所有 domain 進行 SSLPinningevaluators
指定對特定 domain 進行 SSLPinninglet CertificateSessionManager: Session = {
let configuration = URLSessionConfiguration.af.default
configuration.urlCache = nil
let interceptor = SPNetworkInterceptor()
let testSSLPinningDomain = "www.github.com"
// key 是要進行 SSLPinning 的 domain
// value 則是一個 PinnedCertificatesTrustEvaluator,將前面所定義的憑證檔進行驗證
let evaluators: [String: ServerTrustEvaluating] = [
testSSLPinningDomain: PinnedCertificatesTrustEvaluator(certificates: [Certificates.testSSLPinning]))
]
let serverTrustManager = ServerTrustManager(allHostsMustBeEvaluated: false, evaluators: evaluators)
let session = Session(configuration: configuration,
interceptor: interceptor,
serverTrustManager: serverTrustManager,
cachedResponseHandler: ResponseCacher(behavior: .doNotCache))
return session
}()
Public Key Pinning 則僅驗證公開金鑰,不需驗證整張憑證。這使得當憑證需要更新時,只需確保公鑰保持不變,無需經常更新 App。
利用 檢測網站 SSL 憑證安全等級 這個網站取得公鑰 SHA256
導入 https://github.com/datatheorem/TrustKit
pod 'TrustKit'