初識 iOS 開發中的證書固定

引言

在移動應用安全領域,HTTPS/TLS 是數據傳輸的第一道防線,但僅依賴系統默認的證書驗證仍有被中間人(MITM)攻擊的風險。Certificate Pinning(證書固定)通過將客戶端信任“釘”在指定的服務器證書或公鑰上,徹底杜絕了偽造證書帶來的隱患,是面向金融、醫療、支付等高安全場景的必備防護手段。


為什么要做證書固定

  • 防范中間人攻擊(MITM)
    攻擊者可能在公共網絡或被劫持的路由中插入自簽或盜取的證書,繞過系統驗證,讓用戶數據泄露或被篡改。
  • 抵御企業/校園 HTTPS 代理
    有些網絡環境部署了 HTTPS 解密代理,雖然系統層面信任了其根證書,但應用層可通過固定真實服務器證書拒絕代理中轉。
  • 滿足合規審計
    金融、醫療等行業對通信安全有嚴格審計要求,證書固定可證明客戶端僅信任預定義的證書或公鑰。

核心原理與驗證流程

  1. 預嵌入證書或公鑰

    • 將服務器證書(.cer)或對應公鑰哈希(Base64)打包進 App Bundle。
  2. 攔截 TLS 驗證回調

    • URLSessionDelegatedidReceiveChallenge 中,先調用系統的 SecTrustEvaluate 完成基礎驗證,再進行自定義校驗。
  3. 提取公鑰并計算哈希

    • 從服務器返回的證書中提取公鑰數據,對其執行 SHA-256 運算,生成 Base64 編碼的哈希值。
  4. 哈希比對并決策

    • 將計算所得哈希與預置的“釘扎”值對比:

      • 匹配 → 繼續通信(.useCredential(trust:)
      • 不匹配 → 拒絕連接(.cancelAuthenticationChallenge

實現示例

1. 原生 NSURLSession 公鑰固定

class PinnedDelegate: NSObject, URLSessionDelegate {// 本地存儲的公鑰哈希(Base64 編碼)private let pinnedHash = "Base64EncodedPublicKeyHashHere"func urlSession(_ session: URLSession,didReceive challenge: URLAuthenticationChallenge,completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {// 1. 檢查是否為 ServerTrust 驗證guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,let trust = challenge.protectionSpace.serverTrust else {completionHandler(.performDefaultHandling, nil)return}// 2. 系統默認驗證var result = SecTrustResultType.invalidguard SecTrustEvaluate(trust, &result) == errSecSuccess,(result == .unspecified || result == .proceed) else {completionHandler(.cancelAuthenticationChallenge, nil)return}// 3. 提取服務器證書并獲取公鑰數據guard let cert = SecTrustGetCertificateAtIndex(trust, 0),let pubKey = SecCertificateCopyKey(cert),let pubData = SecKeyCopyExternalRepresentation(pubKey, nil) as Data? else {completionHandler(.cancelAuthenticationChallenge, nil)return}// 4. 計算 SHA-256 哈希let hash = sha256(pubData).base64EncodedString()// 5. 與預置哈希對比if hash == pinnedHash {completionHandler(.useCredential, URLCredential(trust: trust))} else {completionHandler(.cancelAuthenticationChallenge, nil)print("🚨 公鑰哈希不匹配,證書固定校驗失敗")}}private func sha256(_ data: Data) -> Data {var buf = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))data.withUnsafeBytes { _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &buf) }return Data(buf)}
}// 使用方式
let session = URLSession(configuration: .default,delegate: PinnedDelegate(),delegateQueue: nil)
session.dataTask(with: URL(string: "https://api.yourdomain.com/data")!).resume()

2. Alamofire 整證書固定

import Alamofire// 1. 從 Bundle 加載本地 .cer
let url = Bundle.main.url(forResource: "server", withExtension: "cer")!
let cert = SecCertificateCreateWithData(nil, try! Data(contentsOf: url) as CFData)!// 2. 配置 ServerTrustManager
let evaluators: [String: ServerTrustEvaluating] = ["api.yourdomain.com":PinnedCertificatesTrustEvaluator(certificates: [cert],acceptSelfSignedCertificates: false,performDefaultValidation: true,validateHost: true)
]let manager = ServerTrustManager(evaluators: evaluators)
let session = Session(serverTrustManager: manager)// 3. 發起請求
session.request("https://api.yourdomain.com/data").validate().responseJSON { response inswitch response.result {case .success:print("?? 證書固定驗證通過")case .failure(let error):print("? 驗證失敗:\(error)")}}

3. AFNetworking 公鑰固定

#import "AFNetworking.h"// 1. 確保將 server.cer 放入 App Bundle
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer  = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];// 2. 配置公鑰固定(Public Key Pinning)
AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];
// 不允許無效證書
policy.allowInvalidCertificates = NO;
// 必須校驗域名
policy.validatesDomainName = YES;
manager.securityPolicy = policy;// 3. 發起 GET 請求
[manager GET:@"https://api.yourdomain.com/data"parameters:nilheaders:nilprogress:nilsuccess:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {NSLog(@"?? 請求成功并通過公鑰固定校驗:%@", responseObject);} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {NSLog(@"? 請求失敗或證書固定校驗失敗:%@", error);}];

實踐建議

  • 優先使用公鑰固定:對證書更新更具兼容性。
  • 結合遠程配置動態更新:如 Firebase Remote Config,下發最新哈希,降低 App 發布頻率。
  • 限定固定規則數量:僅對核心域名或關鍵 CA 進行固定,避免過度復雜。
  • 完整測試與監控:在預發布環境模擬證書換新,確保校驗邏輯可用,并對失敗情況設置告警。

結語

本文從“為什么要做證書固定”到“核心驗證流程”,再到三種主流網絡框架(原生 NSURLSession、Alamofire、AFNetworking)的實戰示例,幫助初學者系統掌握 iOS 證書固定的落地方案。做好 Certificate Pinning,為你的應用網絡通信再添一道牢不可破的安全防線。

擴展閱讀

  • 蘋果官方文檔:Networking and Security → Secure Connections
  • OWASP Mobile Top 10 → M3: Insufficient Cryptography

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/78502.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/78502.shtml
英文地址,請注明出處:http://en.pswp.cn/web/78502.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

單片機的各個種類及其詳細介紹

一、按架構分類的深度解析 1. ARM Cortex-M系列 核心優勢: 統一架構:ARM生態完善,工具鏈(Keil、IAR、GCC)通用。 性能分層:M0(低功耗)、M3(平衡)、M4/M7&am…

5.7/Q1,GBD數據庫最新文章解讀

文章題目:Global, regional, and national burden and trends of rheumatoid arthritis among the elderly population: an analysis based on the 2021 Global Burden of Disease study DOI:10.3389/fimmu.2025.1547763 中文標題:全球、區域…

從微服務到AI服務:Nacos 3.0如何重構下一代動態治理體系?

在現代微服務架構的浪潮中,Nacos早已成為開發者手中的“瑞士軍刀”。作為阿里巴巴開源的核心中間件,它通過動態服務發現、統一配置管理和服務治理能力,為云原生應用提供了堅實的基石。從初創公司到全球500強企業,Nacos憑借其開箱即…

Unity與Unreal Engine(UE)的深度解析及高級用法

以下是Unity與Unreal Engine(UE)的深度解析及高級用法對比,結合技術特性、行業應用與未來發展進行綜合闡述: 一、核心差異與適用場景對比 1. 技術架構與編程模式 Unity 語言與腳本:主要使用C#,語法簡潔且易于學習,適合快速原型開發和中小型項目。支持可視化腳本工具(如…

李沐動手深度學習(pycharm中運行筆記)——05.線性代數

05.線性代數(與課程對應) 1、導入torch import torch2、 標量由只有一個元素的張量表示 x torch.tensor([3.0]) y torch.tensor([2.0]) print("x y:", x y, "\nx * y:", x * y, "\nx / y:", x / y, "\nx ** y…

Python3與Dubbo3.1通訊解決方案(dubbo-python)

【文章非VIP可讀,如果發現閱讀限制為系統自動修改閱讀權限,請留言我改回】 概述 最近AI項目需要java與python通訊,兩邊都是比較新的版本。因此需要雙方進行通訊,在這里記錄一下所采用的方案和關鍵點。 JAVA調用Python python通…

使用 DBeaver 將數據從 PostgreSQL 導出到 SQLite

使用 DBeaver 將數據從 PostgreSQL 導出到 SQLite,可按以下步驟進行: 1、連接到 PostgreSQL 數據庫:打開 DBeaver,點擊 “新建連接”,選擇 “PostgreSQL”,輸入數據庫的地址、端口、用戶名和密碼等信息&am…

介詞:連接名詞與句子其他成分的橋梁

文章目錄 1. with伴隨1.表示“跟人或物”的伴隨2.“行為”和“狀態”的伴隨2. of所屬關系1. 人或物的所屬關系2. 比較抽象的所屬關系3. in1. 在......中,在......范圍里2. 在某一段時間4. on1. 表示地點:在......上2. 表示時間:在某一天3. 關于某個主題5. at1. at + 具體時間…

FastApi快速實踐

文章目錄 一、主要功能:二、安裝 FastAPI 和 Uvicorn(運行服務器)三、示例代碼:四、運行服務器:1. 方式一:2. 方式二: 五、訪問接口六、如果需要跨域(CORS)七、總結 下面…

深度學習中保存最優模型的實踐與探索:以食物圖像分類為例

深度學習中保存最優模型的實踐與探索:以食物圖像分類為例 在深度學習的模型訓練過程中,訓練一個性能良好的模型往往需要耗費大量的時間和計算資源。而保存最優模型不僅可以避免重復訓練,還能方便后續使用和部署。本文將結合食物圖像分類的代…

護理崗位技能比賽主持稿串詞

男:尊敬的各位老師 女:親愛的各位同學 合:大家下午好。 男:在這鳥語花香,詩意盎然的季節里 女:在這陽光燦爛,激情似火的日子里 合:我們歡聚一堂,共同慶祝五一二國際護士節…

【翻譯、轉載】MCP 核心架構

核心架構 了解 MCP 如何連接客戶端、服務器和 LLM 模型上下文協議 (MCP) 構建在一個靈活、可擴展的架構之上,能夠實現 LLM 應用程序與集成之間的無縫通信。本文檔涵蓋了核心的架構組件和概念。 概述 MCP 遵循客戶端-服務器 (client-server) 架構,其中…

Python 數據智能實戰 (11):LLM如何解決模型可解釋性

寫在前面 —— 不只知其然,更要知其所以然:借助 LLM,揭開復雜模型決策的神秘面紗 在前面的篇章中,我們學習了如何利用 LLM 賦能用戶分群、購物籃分析、流失預測以及個性化內容生成。我們看到了 LLM 在理解數據、生成特征、提升模型效果和自動化內容方面的巨大潛力。 然而…

Linux:進程優先級及環境

一:孤兒進程 在Linux系統中,當一個進程創建了子進程后,如果父進程執行完畢或者提前退出而子進程還在運行,那么子進程就會成為孤兒進程。子進程就會被systemd(系統)進程收養,其pid為1 myproces…

Java大廠面試:Java技術棧中的核心知識點

Java技術棧中的核心知識點 第一輪提問:基礎概念與原理 技術總監:鄭薪苦,你對JVM內存模型了解多少?能簡單說說嗎?鄭薪苦:嗯……我記得JVM有堆、棧、方法區這些區域,堆是存放對象的地方&#xf…

CF1000E We Need More Bosses

CF1000E We Need More Bosses 題目描述 題目大意: 給定一個 n n n 個點 m m m 條邊的無向圖,保證圖連通。找到兩個點 s , t s,t s,t,使得 s s s到 t t t必須經過的邊最多(一條邊無論走哪條路線都經過ta,這條邊就是…

imx6uLL應用-v4l2

Linux V4L2 視頻采集 JPEG 解碼 LCD 顯示實踐 本文記錄一個完整的嵌入式視頻處理項目:使用 V4L2 接口從攝像頭采集 MJPEG 圖像,使用 libjpeg 解碼為 RGB 格式,并通過 framebuffer 顯示在 LCD 屏幕上。適用于使用 ARM Cortex-A 系列開發板進…

強化學習機器人模擬器——QAgent:一個支持多種強化學習算法的 Python 實現

QAgent 是一個靈活的 Python 類,專為實現經典的強化學習(Reinforcement Learning, RL)算法而設計,支持 Q-learning、SARSA 和 SARSA(λ) 三種算法。本篇博客將基于提供的 q_agent.py 代碼,詳細介紹 QAgent 類的功能、結構和使用方法,幫助您理解其在強化學習任務中的應用,…

Feign的原理

為什么 SpringCloud 中的Feign,可以幫助我們像使用本地接口一樣調用遠程 HTTP服務? Feign底層是如何實現的?這篇文章,我們一起來聊一聊。 1. Feign 的基本原理 Feign 的核心思想是通過接口和注解定義 HTTP 請求,將接…

探索正態分布:交互式實驗帶你體驗統計之美

探索正態分布:交互式實驗帶你體驗統計之美 正態分布,這條優美的鐘形曲線,可以說是統計學中最重要、最無處不在的概率分布。從自然現象(如身高、測量誤差)到金融市場,再到機器學習,它的身影隨處…