對於安全敏感的數據來講,在與服務端和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
的初始化方法中,你會發現, 網絡
serverTrustPolicyManager
的
SessionManager
。是的,不過,先來看一下
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
須要一個字典來存放這些主機地址,以及對應點的安全策略。在前面咱們已經知道ServerTrustPolicyManager
是URLSession
的一個關聯屬性,那麼它會直接綁定到URLSession
上session
既然安全信任策略有多種,那麼顯然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
這個枚舉類,在它的6
個驗證策略中,對於其中兩個比較經常使用的作個瞭解:
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
公鑰驗證策略,表示客戶端會將服務端返回的證書和本地保存的證書中的 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
方法會去驗證
serverTrust
和
host
:
evaluate
方法代碼量有點多,可是看到
switch
、
case
語句就知道它是對應不一樣的驗證策略來作不一樣的處理。
evaluate
方法會傳入兩個參數一個是服務器的證書,一個是
host
,結果返回一個布爾類型。
從上面的部分能夠知道,正常的驗證策略下,想要完成驗證都要遵循三個步驟:
1️⃣:SecPolicyCreateSSL
建立策略,是否驗證host
2️⃣:SecTrustSetPolicies
爲待驗證的對象SecTrust
設置策略
3️⃣:trustIsValid
進行驗證
在實際開發項目中,可能會有不少公司去購買CA
證書,在請求的時候可能不須要去驗證,但對於自簽證書,咱們能夠根據本身的開發需求進行驗證,其中最安全的是證書鏈加host
雙重驗證。關於Alamofire
的安全認證策略ServerTrustPolicy
就瞭解到這裏,若有錯誤,還請指正!