You need to enable JavaScript to run this app. 如何区分Alamofire证书钉扎失败是MITM攻击还是证书过期
优惠活动
大模型
产品
解决方案
定价
更多

如何区分Alamofire证书钉扎失败是MITM攻击还是证书过期

Alamofire 5 证书钉扎两类错误区分实现方案

核心实现逻辑

原生PinnedCertificatesTrustEvaluator会直接合并所有校验失败为certificatePinningFailed错误,所以需要拆分两个校验步骤:

  • 第一步:先走系统原生的X.509证书信任链校验,判断当前服务端证书本身是否合法可信,这一步失败直接判定为证书不可信(MITM攻击/服务端证书异常)
  • 第二步:系统校验通过后,再执行本地钉扎证书的匹配校验,这一步失败说明服务端证书已经合法更新,但本地App内置的钉扎证书过期,判定为钉扎证书过期(App版本过旧需更新)

自定义信任评估器代码

import Alamofire
import Security

enum CustomTrustError: Error {
    case certificateUntrusted // 证书本身不可信,对应MITM/服务端异常
    case pinnedCertificateExpired // 钉扎证书过期,对应App需更新
}

class CustomPinnedCertEvaluator: ServerTrustEvaluating {
    private let pinnedCertificates: [SecCertificate]
    private let performDefaultValidation: Bool
    
    init(pinnedCertificates: [SecCertificate], performDefaultValidation: Bool = true) {
        self.pinnedCertificates = pinnedCertificates
        self.performDefaultValidation = performDefaultValidation
    }
    
    func evaluate(_ trust: SecTrust, forHost host: String) throws {
        // 第一步:执行系统默认信任校验,判断证书本身是否合法
        if performDefaultValidation {
            let policy = SecPolicyCreateSSL(true, host as CFString)
            SecTrustSetPolicies(trust, policy)
            
            var error: CFError?
            let isSystemTrusted = SecTrustEvaluateWithError(trust, &error)
            guard isSystemTrusted else {
                throw CustomTrustError.certificateUntrusted
            }
        }
        
        // 第二步:校验服务端证书是否和本地钉扎证书匹配
        let serverCertificates = (0..<SecTrustGetCertificateCount(trust)).compactMap { index in
            SecTrustGetCertificateAtIndex(trust, index)
        }
        
        let pinMatch = serverCertificates.contains { serverCert in
            pinnedCertificates.contains { pinnedCert in
                SecCertificateIsEqual(serverCert, pinnedCert)
            }
        }
        
        guard pinMatch else {
            throw CustomTrustError.pinnedCertificateExpired
        }
    }
}

评估器使用配置

初始化Alamofire Session的时候替换默认的评估器即可:

// 加载本地内置的钉扎证书(cer/crt格式都支持)
let pinnedCerts = Bundle.main.certificates(withExtensions: ["cer", "crt"])
let evaluator = CustomPinnedCertEvaluator(pinnedCertificates: pinnedCerts)

let session = Session(
    serverTrustManager: ServerTrustManager(
        evaluators: ["你的业务域名.com": evaluator]
    )
)

错误捕获与提示逻辑

请求失败时解析错误类型给出对应提示:

session.request("你的接口地址").response { response in
    switch response.result {
    case .success(let data):
        // 正常处理业务逻辑
        break
    case .failure(let error):
        if let customError = error.underlyingError as? CustomTrustError {
            switch customError {
            case .certificateUntrusted:
                // 提示当前网络存在风险,建议切换正规网络
                print("当前网络环境不安全,疑似存在中间人攻击")
            case .pinnedCertificateExpired:
                // 提示当前App版本过旧,需要更新到最新版本
                print("当前App版本已不支持服务端安全校验,请更新到最新版本")
            }
        } else {
            // 处理超时、无网络等普通网络错误
        }
    }
}

内容的提问来源于stack exchange,提问作者Fernando

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 12:15:03