安全
在与服务器和 Web 服务通信时使用安全的 HTTPS 连接是保护敏感数据的重要步骤。默认情况下,Alamofire 将使用安全框架提供的 Apple 内置验证来评估服务器提供的证书链。虽然这保证了证书链是有效的,但它并不能防止中间人 (MITM) 攻击或其他潜在漏洞。为了减轻 MITM 攻击,处理敏感客户数据或财务信息的应用程序应使用 ServerTrustPolicy 提供的证书或公钥固定。
服务器信任策略
ServerTrustPolicy 枚举在通过安全 HTTPS 连接连接到服务器时评估通常由 URLAuthenticationChallenge 提供的服务器信任。
let serverTrustPolicy = ServerTrustPolicy.pinCertificates(
certificates: ServerTrustPolicy.certificates(),
validateCertificateChain: true,
validateHost: true
)
有许多不同的服务器信任评估案例让您可以完全控制验证过程:
- performDefaultEvaluation:使用默认服务器信任评估,同时允许您控制是否验证挑战提供的主机。
- pinCertificates:使用固定证书来验证服务器信任。如果固定证书之一与服务器证书之一匹配,则认为服务器信任有效。
- pinPublicKeys:使用固定的公钥来验证服务器信任。如果固定的公钥之一与服务器证书公钥之一匹配,则服务器信任被认为是有效的。
- disableEvaluation:禁用所有评估,这反过来将始终将任何服务器信任视为有效。
- customEvaluation:使用关联的闭包来评估服务器信任的有效性,从而使您可以完全控制验证过程。谨慎使用。
服务器信任策略管理器
ServerTrustPolicyManager 负责存储服务器信任策略到特定主机的内部映射。这允许 Alamofire 根据不同的服务器信任策略评估每个主机。
let serverTrustPolicies: [String: ServerTrustPolicy] = [
"test.example.com": .pinCertificates(
certificates: ServerTrustPolicy.certificates(),
validateCertificateChain: true,
validateHost: true
),
"insecure.expired-apis.com": .disableEvaluation
]
let sessionManager = SessionManager(
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)
确保保留对新 SessionManager 实例的引用,否则当您的 sessionManager 被释放时,您的请求将全部取消。这些服务器信任策略将导致以下行为:
test.example.com 将始终使用启用证书链和主机验证的证书固定,因此需要满足以下条件才能使 TLS 握手成功:证书链必须有效。证书链必须包含固定证书之一。质询主机必须与证书链的叶证书中的主机匹配。insecure.expired-apis.com 永远不会评估证书链,并且始终允许 TLS 握手成功。所有其他主机将使用 Apple 提供的默认评估。子类化服务器信任策略管理器
如果您发现自己需要更灵活的服务器信任策略匹配行为(即通配符域),则将 ServerTrustPolicyManager 子类化并使用您自己的自定义实现覆盖 serverTrustPolicyForHost 方法。
class CustomServerTrustPolicyManager: ServerTrustPolicyManager {
override func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
var policy: ServerTrustPolicy?
// Implement your custom domain matching behavior...
return policy
}
}
验证主机
.performDefaultEvaluation、.pinCertificates 和 .pinPublicKeys 服务器信任策略都采用 validateHost 参数。将该值设置为 true 将导致服务器信任评估验证证书中的主机名是否与质询的主机名匹配。如果它们不匹配,则评估将失败。validateHost 值为 false 仍将评估完整的证书链,但不会验证叶证书的主机名。
建议在生产环境中始终将 validateHost 设置为 true。验证证书链
固定证书和公钥都可以选择使用 validateCertificateChain 参数验证证书链。通过将此值设置为 true,除了对固定证书或公钥执行字节相等检查外,还将评估完整的证书链。值为 false 将跳过证书链验证,但仍会执行字节相等检查。
在某些情况下,禁用证书链验证可能是有意义的。禁用验证的最常见用例是自签名证书和过期证书。在这两种情况下,评估总是会失败,但字节相等检查仍将确保您从服务器接收到您期望的证书。
建议在生产环境中始终将 validateCertificateChain 设置为 true。
应用传输安全
在 iOS 9 中添加 App Transport Security (ATS) 后,使用带有多个 ServerTrustPolicy 对象的自定义 ServerTrustPolicyManager 可能无效。如果您不断看到 CFNetwork SSLHandshake failed (-9806) 错误,您可能遇到了这个问题。Apple 的 ATS 系统会覆盖整个质询系统,除非您在应用程序的 plist 中配置 ATS 设置以禁用足够多的功能以允许您的应用程序评估服务器信任。
如果您遇到此问题(自签名证书的可能性很高),您可以通过将以下内容添加到 Info.plist 来解决此问题。
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>example.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
<!-- Optional: Specify minimum TLS version -->
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
</dict>
</dict>
</dict>
</dict>
是否需要将 NSExceptionRequiresForwardSecrecy 设置为 NO 取决于您的 TLS 连接是否使用了允许的密码套件。在某些情况下,需要将其设置为 NO。NSExceptionAllowsInsecureHTTPLoads 必须设置为 YES 以允许 SessionDelegate 接收挑战回调。一旦挑战回调被调用,ServerTrustPolicyManager 将接管服务器信任评估。如果您尝试连接到仅支持低于 1.2 的 TLS 版本的主机,您可能还需要指定 NSTemporaryExceptionMinimumTLSVersion。
建议在生产环境中始终使用有效证书。在本地网络中使用自签名证书
如果您尝试连接到在您的本地主机上运行的服务器,并且您使用的是自签名证书,则需要将以下内容添加到您的 Info.plist。
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
</dict>
根据 Apple 文档,将 NSAllowsLocalNetworking 设置为 YES 允许加载本地资源,而无需为应用程序的其余部分禁用 ATS。
参考:-
https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md
有关实现细节,请参阅测试。
https://github.com/Alamofire/Alamofire/blob/master/Tests/TLSEvaluationTests.swift#L290-L450