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就了解到这里,若有错误,还请指正!

相关文章
相关标签/搜索