Alamofire學習--安全認證策略ServerTrustPolicy

前言

對於安全敏感的數據來講,在與服務端和Web服務交互時使用安全的HTTPS鏈接是很是重要的一步。默認狀況下,Alamofire會使用蘋果安全框架內置的驗證方法來驗證服務端提供的證書鏈。雖然保證了證書鏈是有效的,可是也不能防止中間人攻擊,爲了減小中間人攻擊,處理用戶的敏感數據時應該使用安全策略(ServerTrustPolicy)來作證書(certificate)驗證。swift

安全策略

引入安全策略ServerTrustPolicy以前,先來個小例子🌰熱熱身,否則沒法展開啊,先建立一個請求:數組

let urlString = "https://47.105.168.156:20199/users/bar"
SessionManager.default.request(urlString).response { (responseString) in
    print(responseString)
}
複製代碼

結果呢,來看: 安全

哎呀,出現了錯誤,(這裏已經在 plist文件中把 Allow Arbitrary Loads設置爲 YES了哦);那麼爲何呢? 由於 https://47.105.168.156:20199/users/bar這是一個自簽名的地址。(要的就是這種效果😁😁😁)

因爲全部的網絡請求都會走SessionDelegate的回調,在SessionDelegate.swift文件中就有這樣一個方法:bash

這個方法內部表示若是 taskDidReceiveChallenge存在,就說明用戶本身來處理證書的事情,不然 TaskDelegate會進行任務下發,那麼就來到了 TaskDelegate.swift
從代碼判斷邏輯應該不難看出最重要的其實就是 let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),若是這個條件不成立,就會跳出執行,那麼這個回調也就直接完成了。

若是點擊serverTrustPolicyManager跟進去會發現,它是URLSession的一個關聯屬性,那麼這個serverTrustPolicyManager是在何時傳入的呢? 服務器

既然它是URLSession的一個關聯屬性,那麼根據上面寫的例子🌰,先從SessionManager來看,在SessionManager的初始化方法中,你會發現, 網絡

呦呵,這麼容易就找到了,完美!那麼說,咱們能夠初始化一個帶有 serverTrustPolicyManagerSessionManager。是的,不過,先來看一下 ServerTrustPolicyManager這個類:

open class ServerTrustPolicyManager {
    // 信任策略數組
    open let policies: [String: ServerTrustPolicy]
    
    初始化方法
    public init(policies: [String: ServerTrustPolicy]) {
        self.policies = policies
    }

    // 根據主機地址返回對應的信任策略
    open func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
        return policies[host]
    }
}
複製代碼

若是咱們把ServerTrustPolicy當作是一個安全策略,就是指對一個服務器採起的策略。那麼ServerTrustPolicyManager是對ServerTrustPolicy的管理者。然而在現實環境中,一個APP可能會用到不少不一樣的主機地址。所以就產生了這樣的需求,爲每個主機地址都綁定一個特定的安全策略。所以,ServerTrustPolicyManager須要一個字典來存放這些主機地址,以及對應點的安全策略。在前面咱們已經知道ServerTrustPolicyManagerURLSession的一個關聯屬性,那麼它會直接綁定到URLSessionsession

既然安全信任策略有多種,那麼顯然ServerTrustPolicy是一種枚舉類型:框架

public enum ServerTrustPolicy {
    // 執行默認的策略,合法證書經過驗證
    case performDefaultEvaluation(validateHost: Bool)
    // 執行撤銷策略,對註銷證書作的特殊處理
    case performRevokedEvaluation(validateHost: Bool, revocationFlags: CFOptionFlags)
    // 證書驗證策略,使用證書來驗證服務器信任
    case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool)
    // 公鑰驗證策略,使用公鑰來驗證服務器信任
    case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool)
    // 禁用策略,不作任何驗證(心真大...)
    case disableEvaluation
    // 自定義策略,返回一個BOOL值
    case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool)
    
    //此處省略代碼不知道多少行......
}
複製代碼

瞭解了上面這些內容,那麼下面是否是能夠初始化一個SessionManager了:函數

let urlString = "https://47.105.168.156:20199/users/bar"
 let serverTrustPolicies: [String: ServerTrustPolicy] = [
            urlString: .pinCertificates(
                certificates: ServerTrustPolicy.certificates(),
                validateCertificateChain: true,
                validateHost: true
        )
    ]
let sessionManager = SessionManager(serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies))
sessionManager.request(urlString).response { (responseString) in
    print(responseString)
}
複製代碼

這樣就能夠了。😏😼😼ui

ServerTrustPolicy

從上面⤴️⤴️⤴️咱們已經大概瞭解了ServerTrustPolicy這個枚舉類,在它的6個驗證策略中,對於其中兩個比較經常使用的作個瞭解:

pinCertificates

pinCertificates是證書驗證策略,表明客戶端會將服務端返回的證書和本地保存的證書中的全部內容 所有進行校驗,若是正確驗證,才繼續執行。

pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool)
複製代碼

1️⃣參數1:certificates表明的是證書

2️⃣參數2:validateCertificateChain表明是否驗證證書鏈

3️⃣參數3:validateHost表明是否驗證子地址

那麼既然須要傳入證書,怎麼找到這個證書文件呢? 在ServerTrustPolicy中有這樣一個方法certificates

這個 certificates方法會獲取到全部根目錄下的證書。

在實際開發中,若是在和服務端的安全鏈接過程當中,須要對服務端進行驗證,比較好的辦法就是在本地保存一些證書,接着拿到服務器傳過來的證書,而後進行對比驗證,若是驗證成功,就表示能夠信任該服務端。從上邊的函數中能夠看出,Alamofire會在Bundle中查找帶有".cer", ".CER", ".crt", ".CRT", ".der", ".DER"後綴的證書

pinPublicKeys

pinPublicKeys 公鑰驗證策略,表示客戶端會將服務端返回的證書和本地保存的證書中的 PublicKey部分 進行校驗,若是驗證正確,才繼續執行。

case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool)
複製代碼

1️⃣參數1:publicKeys表示公鑰

2️⃣參數2:validateCertificateChain表明是否驗證證書鏈

3️⃣參數3:validateHost 表明是否驗證子地址

一樣的,ServerTrustPolicy中有這樣一個方法publicKeys用來查找全部根目錄下證書的公鑰

這裏說明一下validateCertificateChain驗證證書鏈,若是服務端沒有配置好證書鏈,那麼就不能驗證證書鏈,也驗證不了,會直接取消驗證。

驗證流程

上面的內容已經對驗證策略瞭解的差很少了,如今把視線拉回到最開始的時候,咱們知道let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),這個條件不成立才致使驗證不成功,如今改變了驗證策略以後呢,代碼繼續走下去看看會發生什麼?

在這裏 evaluate方法會去驗證 serverTrusthost

evaluate方法代碼量有點多,可是看到 switchcase語句就知道它是對應不一樣的驗證策略來作不一樣的處理。 evaluate方法會傳入兩個參數一個是服務器的證書,一個是 host,結果返回一個布爾類型。

從上面的部分能夠知道,正常的驗證策略下,想要完成驗證都要遵循三個步驟:

1️⃣:SecPolicyCreateSSL 建立策略,是否驗證host

2️⃣:SecTrustSetPolicies 爲待驗證的對象SecTrust設置策略

3️⃣:trustIsValid進行驗證

總結

在實際開發項目中,可能會有不少公司去購買CA證書,在請求的時候可能不須要去驗證,但對於自簽證書,咱們能夠根據本身的開發需求進行驗證,其中最安全的是證書鏈加host雙重驗證。關於Alamofire的安全認證策略ServerTrustPolicy就瞭解到這裏,若有錯誤,還請指正!

相關文章
相關標籤/搜索