网络安全
问题
iOS 网络安全有哪些措施?如何实现 SSL Pinning?
答案
iOS 网络安全层次
| 层次 | 措施 |
|---|---|
| 传输层 | HTTPS / TLS 加密 |
| 系统级 | App Transport Security(ATS) |
| 应用级 | SSL Pinning(证书固定) |
| 数据级 | 敏感数据加密存储 |
SSL Pinning 实现
固定服务器证书的公钥,防止中间人攻击:
class NetworkDelegate: NSObject, URLSessionDelegate {
// 服务器公钥的 SHA256 指纹
private let pinnedKeyHash = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
let serverTrust = challenge.protectionSpace.serverTrust else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
// 获取服务器证书的公钥
if let serverKey = SecTrustCopyKey(serverTrust) {
let serverKeyData = SecKeyCopyExternalRepresentation(serverKey, nil)! as Data
let keyHash = serverKeyData.sha256().base64EncodedString()
if keyHash == pinnedKeyHash {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
return
}
}
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
Alamofire SSL Pinning
let evaluators: [String: ServerTrustEvaluating] = [
"api.example.com": PublicKeysTrustEvaluator() // 公钥固定
// 或 PinnedCertificatesTrustEvaluator() // 证书固定
]
let manager = ServerTrustManager(evaluators: evaluators)
let session = Session(serverTrustManager: manager)
证书固定的维护
证书有有效期,更换证书后旧版 App 会无法连接。推荐固定公钥(而非证书本身),因为续期证书时公钥通常不变。同时在 App 中预置多个备用公钥。
常见面试问题
Q1: HTTPS 能完全防止中间人攻击吗?
答案:不能。如果用户安装了恶意 CA 证书(如公司代理),中间人可以签发被系统信任的伪造证书。SSL Pinning 可以防御这种攻击——即使中间人的证书被系统信任,公钥指纹不匹配也会拒绝连接。
Q2: 如何在 Charles/Proxyman 中调试 HTTPS?
答案:
- 在设备上安装并信任 Charles 的 CA 证书
- 如果 App 使用了 SSL Pinning,需要在 Debug 环境禁用 Pinning
- 可以通过编译宏
#if DEBUG控制是否启用 Pinning