对于安全敏感的数据来讲,在与服务端和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
就了解到这里,若有错误,还请指正!