iOS之AFSecurityPolicy

来源:https://www.cnblogs.com/lizheng114/archive/2018/04/16/8845894.html
-Advertisement-
Play Games

AFSecurityPolicy是AFNetworking中負責對https請求進行證書驗證的模塊,本文主要是要搞清楚它是如何工作的。 在介紹AFSecurityPolicy之前,我們先來瞭解一下https以及一些相關概念。 HTTPS 簡單來說,https是運行在SSL/TLS之上的http,是為 ...


AFSecurityPolicy是AFNetworking中負責對https請求進行證書驗證的模塊,本文主要是要搞清楚它是如何工作的。

在介紹AFSecurityPolicy之前,我們先來瞭解一下https以及一些相關概念。

HTTPS

簡單來說,https是運行在SSL/TLS之上的http,是為了提升數據傳輸的安全性的,使用到了對稱加密和非對稱加密演算法。讓我們通過客戶端與服務端的四次交互(四次握手)來詳細看看https都做了些什麼。

重點說下第2步和第3步。

第2步主要是要將服務端的公鑰安全的發送給客戶端,為此服務端主要做了兩件事:

1、用服務端的私鑰加密摘要

2、傳遞CA頒發的用CA的私鑰加密的包含服務端公鑰的證書

然後到了客戶端(也就是第3步),客戶端分幾步來驗證:

1、客戶端先使用本地CA來驗證證書是否授信,如果是權威機構頒發的證書,客戶端會認為該證書是授信的(如果是自己搭建的CA,則會被認為是不授信的,會產生警告),同時也會驗證證書的有效期,訪問的功能變數名稱是否一致等信息。

2、客戶端根據證書的要求生成證書編號

3、然後客戶端會用本地CA公鑰解密證書,拿到證書編號,如果生成的證書編號和拿到的證書編號一致,則認為證書沒有問題,從而拿到服務端的公鑰簡稱為SK

4、然後使用SK來解密服務端私鑰加密的摘要(簡稱SS),並且本地用加密演算法將內容加密成摘要(簡稱LS),對比SS == LS

需要說明一下CA的私鑰加密的包含服務端公鑰的證書,這個證書是用來保證信息不被中間人掉包的。因為證書編號是由CA的私鑰加密的,即使是中間人也無法拿到CA的私鑰,而客戶端的本地CA公鑰只能解密由CA的私鑰加密的證書編號,所以中間人無法偽造證書。 

那麼假設中間人自己也申請一個CA的證書,然後客戶端請求的時候本來要請求服務端的證書A,中間人攔截以後,發回自己的證書B給客戶端,這個時候對於證書編號的驗證就不管用了,但是,證書A和證書B的功能變數名稱是不同的,所以客戶端在做驗證的時候,就會認為證書不授信。

以上這些繞來繞去的流程就是https請求保證數據安全和防篡改的簡易流程。也可以參考:

https://www.cnblogs.com/zhangshitong/p/6478721.html

 

我們在瞭解https的時候,會接觸到一些相關常見的概念,如:SSL/TLS,openssl,PKI,CA,X.509等,下麵我們來簡單瞭解一下。

HTTPS相關概念

SSL:(Secure Socket Layer,安全套接字層),用以保障在Internet上數據傳輸安全。

TLS:(Transport Layer Security,傳輸層安全協議),用於兩個應用程式之間提供保密性和數據完整性。簡單來看,可以認為TLS是SSL的升級版,比SSL更加安全。iOS9以後,已經要求TLS版本不低於1.2。

關於SSL和TLS的詳情,可以參看:

http://www.cocoachina.com/ios/20150918/13488.html

http://seanlook.com/2015/01/07/tls-ssl/

PKI:(Public Key Infrastructure,公開密鑰基礎設施),是一個標準,用以為所有網路應用提供加密和數字簽名等密碼服務及所必須的秘鑰和證書管理體系。CA是PKI的核心。

CA:(Certificate Authority,證書認證中心),是一個負責發放和管理數字證書的第三方權威機構,它負責管理PKI結構下的所有用戶(包括各種應用程式)的證書,把用戶的公鑰和用戶的其他信息捆綁在一起,在網上驗證用戶的身份。CA機構的數字簽名使得攻擊者不能偽造和篡改證書。前文所說的服務端證書,就是由CA頒發的。

關於PKI和CA的詳情,可以參看:

http://netsecurity.51cto.com/art/200602/21066.htm

X.509:是PKI體系中最重要的標準。是一些標準欄位的集合,這些欄位包含有關用戶或設備及其相應公鑰的信息。包含:版本號,公鑰,演算法,序列號,主題信息,有效期,認證機構,數字簽名等。可以參考:https://baike.baidu.com/item/x509/1240109?fr=aladdin

openssl:一個強大的安全套接字層密碼庫,大概可以分成三個主要的功能部分。

1、libcryto,這是一個具有通用功能的加密庫,裡面實現了眾多的加密庫。

2、libssl,這個是實現ssl機制的,它是用於實現TLS/SSL的功能。

3、openssl,是個多功能命令行工具,它可以實現加密解密,甚至還可以當CA來用,可以讓你創建證書、吊銷證書。

openssl生成私鑰,公鑰,並加密解密的簡單實用如下:

openssl genrsa -out rsakey0.pem 1024        //生成1024位rsa私鑰
openssl rsa -in rsakey0.pem -pubout  -out rsaKeyPublic0        //生成公鑰
openssl rsautl -encrypt -in 1.txt -inkey rsaKeyPublic0.pem -pubin -out 2.txt    //公鑰加密文件
openssl rsautl -decrypt -in 2.txt -inkey rsaKey0.pem -out 3.txt        //私鑰解密文件

 

在瞭解了https的過程及相關的概念作為鋪墊以後,下麵我們來看看AFSecurityPolicy到底做了什麼。

AFSecurityPolicy

蘋果已經為我們封裝好了https連接的建立,加密解密的過程,但是並沒有為我們驗證證書是否合法,這一步需要在AFSecurityPolicy裡面完成。

當實用AFNetworking來發起https的請求時,會調用委托:

- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler{
}

這是一個質詢,需要確認認證信息才能完成連接。

//判斷伺服器返回的證書類型,是否信任
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                if (credential) {
                    disposition = NSURLSessionAuthChallengeUseCredential;//使用指定的證書
                } else {
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;//預設處理方式(忽略證書)
                }
            } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;//取消質詢
            }
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }

重點看下:

//判斷服務端來的證書是否驗證通過
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain:(NSString *)domain
{
    if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
        NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
        return NO;
    }

    NSMutableArray *policies = [NSMutableArray array];
//是否驗證功能變數名稱,如果你的請求要直接用ip去連,可以忽略功能變數名稱驗證,但是有風險,我們之前說過;下麵無論if還是else都是創建不同的驗證策略。
    if (self.validatesDomainName) {
        [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
    } else {
        [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
    }

//設置驗證策略
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);

    if (self.SSLPinningMode == AFSSLPinningModeNone) {
//客戶端是否信任無效或過期的證書(可能是自簽名證書)或者校驗伺服器傳遞的安全信息 serverTrust 是否是有效。 
        return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
    } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
        return NO;
    }

    switch (self.SSLPinningMode) {
        case AFSSLPinningModeNone:
        default:
            return NO;
        case AFSSLPinningModeCertificate: {
//驗證證書是否和本地的證書相同
            NSMutableArray *pinnedCertificates = [NSMutableArray array];
            for (NSData *certificateData in self.pinnedCertificates) {
                [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
            }
//將本地證書的數據設置為校驗伺服器安全信息 serverTrust 的錨證書
            SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);

            if (!AFServerTrustIsValid(serverTrust)) {
                return NO;
            }

            //獲取所有服務端的證書,後面用以比較是否包含
            NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
            
            for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
                if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
                    return YES;
                }
            }
            
            return NO;
        }
        case AFSSLPinningModePublicKey: {
//驗證本地證書的公鑰和服務端的公鑰是否相同
            NSUInteger trustedPublicKeyCount = 0;
//獲取服務端的證書公鑰
            NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);

            for (id trustChainPublicKey in publicKeys) {
                for (id pinnedPublicKey in self.pinnedPublicKeys) {
                    if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
                        trustedPublicKeyCount += 1;
                    }
                }
            }
            return trustedPublicKeyCount > 0;
        }
    }
    
    return NO;
}

註釋基本都加了,這裡要說一下:SSLPinningMode(校驗策略),分三種

AFSSLPinningModeNone: 這個模式表示不做SSL pinning,只跟瀏覽器一樣在系統的信任機構列表裡驗證服務端返回的證書。若證書是信任機構簽發的就會通過,若是自己伺服器生成的證書,這裡是不會通過的。

AFSSLPinningModeCertificate:這個模式表示用證書綁定方式驗證證書,需要客戶端保存有服務端的證書拷貝,這裡驗證分兩步,第一步驗證證書的功能變數名稱/有效期等信息,第二步是對比服務端返回的證書跟客戶端返回的是否一致。需要考慮證書過期的問題,如果過期了,要想辦法讓app發起一個http請求,將續費的證書下載到沙盒中就可以了。

AFSSLPinningModePublicKey:這個模式同樣是用證書綁定方式驗證,客戶端要有服務端的證書拷貝,只是驗證時只驗證證書里的公鑰,不驗證證書的有效期等信息。不需要考慮證書過期

我們來回顧一下AFSecurityPolicy的本地證書驗證和我們之前提的https請求本地驗證有什麼不同。

https本地驗證:是否權威機構的證書、功能變數名稱是否一致、證書編號是否一致,都一致的話,就可以拿到服務端的公鑰

AFSecurityPolicy驗證:是否權威機構證書、功能變數名稱是否一致(如果不驗證功能變數名稱,則忽略)、公鑰驗證或者證書驗證

綠色的部分就是有差異的地方,其實證書編號是否一致蘋果在底層已經做了。

如果選擇AFSSLPinningModeNone,則兩者是基本一致的,這也是預設策略。

但是如果選擇其他兩種,就表示在app內部放置了服務端的公鑰證書(因為一般app請求的功能變數名稱不會有太多,一般都是一個),這樣的話就需要比較公鑰證書或者公鑰本身了,所以會多出來一步。但是這樣做更加安全,對於防範中間人攻擊更有效,回顧一下本文https部分應該比較容易理解。

AFSecurityPolicy的參考文章:

https://blog.csdn.net/u011374318/article/details/79364995

https://www.cnblogs.com/oc-bowen/p/5896041.html

 

最後,當我們通過瞭解https的請求過程,瞭解相關知識,瞭解如何防範中間人攻擊,繞了這麼大一圈後,再來理解AFSecurityPolicy,會發覺容易很多。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 界面效果: 佈局代碼: MainActivity: 讀寫文件工具類: ...
  • 在JavaEE中,有一個Junit測試包 而在開發安卓中,我們要使用谷歌公司開發好的一些類 代碼如下: 這裡要測試一個計算器類: 在配置文件下添加這一行: 在外部加上這幾行: 寫一個測試類: 測試成功! ...
  • 1:線性佈局 2.相對佈局: 這兩種最常用,其他的如下: 3.幀佈局:重疊在一起 後兩種不常用:表格、絕對佈局,實際開發已過時 ...
  • android kl(key layout)文件是一個映射文件,是標準linux與anroid的鍵值映射文件,kl文件可以有很多個,但是它有一個使用優先順序: 如果你沒有為設備單獨定義kl文件,那麼就會使用預設的那個Generic.kl文件。 例如: 還有一點需要註意,我們怎麼知道這個vendor號和 ...
  • 註意設置許可權: 佈局: 上邊採用的是按鈕點擊事件第一種:內部類 第二種:匿名內部類 第三種:實現介面(多按鈕時候推薦使用) 第四種:聲明方法 ...
  • 最近發現越來越多的朋友,在使用PC端的小程式多客服系統,有的朋友也在後臺問,有沒有一款,手機端的工具,那樣才是真正的隨時隨地處理客戶的消息。 剛好,有看到一款比較用心的手機端工具,特點具有類似QQ聊天界面,溝通無障礙,使用無門檻,回覆消息,快速高效。重點是收到新消息提醒,有提示音。 芝麻小客服 體驗 ...
  • app 下載更新 file-downloader 文件下載庫的簡單介紹和使用 ...
  • Android Studio新建或者打開項目的時候,一直卡在Building "" Gradle project info 進度上不動,猜測是網路原因下載gradle不成功。 兩種解決方法: 1、使用本地distributionUrl:找到distributionUrl對應路徑(在gradle-wr ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...