iOS安全系列之一:HTTPS

  1. HTTPS
    其實HTTPS從最終的數據解析的角度,與HTTP沒有任何的區別,HTTPS就是將HTTP協議數據包放到SSL/TSL層加密后,在TCP/IP層組成IP數據報去傳輸,以此保證傳輸數據的安全;而對于接收端,在SSL/TSL將接收的數據包解密之后,將數據傳給HTTP協議層,就是普通的HTTP數據。HTTP和SSL/TSL都處于OSI模型的應用層。從HTTP切換到HTTPS是一個非常簡單的過程,在做具體的切換操作之前,我們需要了解幾個概念:

SSL/TSL

關于SSL/TSL,阮一峰的兩篇博客文章做了很好的介紹:
SSL/TLS協議運行機制的概述
圖解SSL/TLS協議
簡單的來說,SSL/TSL通過四次握手,主要交換三個信息:
數字證書:該證書包含了公鑰等信息,一般是由服務器發給客戶端,接收方通過驗證這個證書是不是由信賴的CA簽發,或者與本地的證書相對比,來判斷證書是否可信;假如需要雙向驗證,則服務器和客戶端都需要發送數字證書給對方驗證;

三個隨機數:

這三個隨機數構成了后續通信過程中用來對數據進行對稱加密解密的“對話密鑰”。
首先客戶端先發第一個隨機數N1,然后服務器回了第二個隨機數N2(這個過程同時把之前提到的證書發給客戶端),這兩個隨機數都是明文的;而第三個隨機數N3(這個隨機數被稱為Premaster secret),客戶端用數字證書的公鑰進行非對稱加密,發給服務器;而服務器用只有自己知道的私鑰來解密,獲取第三個隨機數。這樣,服務端和客戶端都有了三個隨機數N1+N2+N3,然后兩端就使用這三個隨機數來生成“對話密鑰”,在此之后的通信都是使用這個“對話密鑰”來進行對稱加密解密。因為這個過程中,服務端的私鑰只用來解密第三個隨機數,從來沒有在網絡中傳輸過,這樣的話,只要私鑰沒有被泄露,那么數據就是安全的。
加密通信協議:就是雙方商量使用哪一種加密方式,假如兩者支持的加密方式不匹配,則無法進行通信;
有個常見的問題,關于隨機數為什么要三個?只最后一個隨機數N3不可以么?
這是由于SSL/TLS設計,就假設服務器不相信所有的客戶端都能夠提供完全隨機數,假如某個客戶端提供的隨機數不隨機的話,就大大增加了“對話密鑰”被破解的風險,所以由三組隨機數組成最后的隨機數,保證了隨機數的隨機性,以此來保證每次生成的“對話密鑰”安全性。

數字證書

數字證書是一個電子文檔,其中包含了持有者的信息、公鑰以及證明該證書有效的數字簽名。而數字證書以及相關的公鑰管理和驗證等技術組成了PKI(公鑰基礎設施)規范體系。一般來說,數字證書是由數字證書認證機構(Certificate authority,即CA)來負責簽發和管理,并承擔PKI體系中公鑰合法性的檢驗責任;數字證書的類型有很多,而HTTPS使用的是SSL證書。
怎么來驗證數字證書是由CA簽發的,而不是第三方偽造的呢? 在回答這個問題前,我們需要先了解CA的組織結構。首先,CA組織結構中,最頂層的就是根CA,根CA下可以授權給多個二級CA,而二級CA又可以授權多個三級CA,所以CA的組織結構是一個樹結構。對于SSL證書市場來說,主要被Symantec(旗下有VeriSign和GeoTrust)、Comodo SSL、Go Daddy 和 GlobalSign 瓜分。 了解了CA的組織結構后,來看看數字證書的簽發流程:

數字證書的簽發流程

數字證書的簽發機構CA,在接收到申請者的資料后進行核對并確定信息的真實有效,然后就會制作一份符合X.509標準的文件。證書中的證書內容包含的持有者信息和公鑰等都是由申請者提供的,而數字簽名則是CA機構對證書內容進行hash加密后得到的,而這個數字簽名就是我們驗證證書是否是有可信CA簽發的數據。

數字證書的驗證流程

假設上圖證書是由證書簽發機構CA1簽發的。
1)接收端接到一份數字證書Cer1后,對證書的內容做Hash得到H1;
2)從簽發該證書的機構CA1的數字證書中找到公鑰,對證書上數字簽名進行解密,得到證書Cer1簽名的Hash摘要H2;
3)對比H1和H2,如相等,則表示證書沒有被篡改。
4)但這個時候還是不知道CA是否是合法的,我們看到上圖中有CA機構的數字證書,這個證書是公開的,所有人都可以獲取到。而這個證書中的數字簽名是上一級生成的,所以可以這樣一直遞歸驗證下去,直到根CA。根CA是自驗證的,即他的數字簽名是由自己的私鑰來生成的。合法的根CA會被瀏覽器和操作系統加入到權威信任CA列表中,這樣就完成了最終的驗證。所以,一定要保護好自己環境(瀏覽器/操作系統)中根CA信任列表,信任了根CA就表示信任所有根CA下所有子級CA所簽發的證書,不要隨便添加根CA證書。
一般操作系統和瀏覽器只包含根CA機構的證書,而在配置Web服務器的HTTPS時,也會將配置整個證書鏈,所以整個校驗流程是從最后的葉子節點證書開始,用父節點校驗子節點,一層層校驗整個證書鏈的可信性。
打個比喻:父(根CA數字證書)-子(CA數字證書)-孫(數字證書)三代人,假設父沒有其他兄弟(相當于根CA機構是唯一的),假如子與父進行DNA親子鑒定,檢測DNA位點(即證書簽名)相同,那就基本確定子是由父所生;孫與子一樣。這樣就能夠確定孫肯定是源于父一脈,是父(根CA數字證書)的合法繼承人。數字證書的驗證就是基于同樣的原理。

Basic Constraint校驗漏洞

那是否不管多少層都可以這樣一直信任下去呢?理論上是可行的,但會遇到一個問題。假設我從可信CA機構購買了一張證書,使用這張證書簽發的證書是否也會被操作系統和瀏覽器信任呢?明顯是不應該相信的,因為我并不是CA機構,假如我簽發的證書也被信任的話,那我完全可以自己簽發任何域名的證書來進行偽造攻擊。這就是著名的Basic Constraint校驗漏洞,X.509證書中的Basic Constraint包含了這是不是一個CA機構,以及有效證書路徑的最大深度(即,這個CA還能否繼續簽發CA機構證書及其簽發子CA證書的路徑深度)。但在幾年前,包括微軟和Apple都爆出了沒有正確校驗這些信息的漏洞。
了解了上面關于SSL/TSL通信加密策略以及數字證書的概念之后,對HTTPS的安全機制就有了個初步的了解,下面我們看如何在iOS上實現對HTTPS的支持。
  1. 實現支持HTTPS
    首先,需要明確你使用HTTP/HTTPS的用途,因為OSX和iOS平臺提供了多種API,來支持不同的用途,官方文檔《Making HTTP and HTTPS Requests》有詳細的說明,而文檔《HTTPS Server Trust Evaluation》則詳細講解了HTTPS驗證相關知識,這里就不多說了。本文主要講解我們最常用的NSURLConnection支持HTTPS的實現(NSURLSession的實現方法類似,只是要求授權證明的回調不一樣而已),以及怎么樣使用AFNetworking這個非常流行的第三方庫來支持HTTPS。本文假設你對HTTP以及NSURLConnection的接口有了足夠的了解。驗證證書的相關Api在Security Framework中,驗證流程如下:
1). 第一步,先獲取需要驗證的信任對象(Trust Object)。這個Trust Object在不同的應用場景下獲取的方式都不一樣,對于NSURLConnection來說,是從delegate方法-connection:willSendRequestForAuthenticationChallenge:回調回來的參數challenge中獲取([challenge.protectionSpace serverTrust])。
2). 使用系統默認驗證方式驗證Trust Object。SecTrustEvaluate會根據Trust Object的驗證策略,一級一級往上,驗證證書鏈上每一級數字簽名的有效性(上一部分有講解),從而評估證書的有效性。
3). 如第二步驗證通過了,一般的安全要求下,就可以直接驗證通過,進入到下一步:使用Trust Object生成一份憑證([NSURLCredential credentialForTrust:serverTrust]),傳入challenge的sender中([challenge.sender useCredential:cred forAuthenticationChallenge:challenge])處理,建立連接。
4). 假如有更強的安全要求,可以繼續對Trust Object進行更嚴格的驗證。常用的方式是在本地導入證書,驗證Trust Object與導入的證書是否匹配。更多的方法可以查看Enforcing Stricter Server Trust Evaluation,這一部分在講解AFNetworking源碼中會講解到。
5). 假如驗證失敗,取消此次Challenge-Response Authentication驗證流程,拒絕連接請求。

ps: 假如是自建證書的,則不使用第二步系統默認的驗證方式,因為自建證書的根CA的數字簽名未在操作系統的信任列表中。
iOS授權驗證的API和流程大概了解了,下面,我們看看在NSURLConnection中的代碼實現:

// Now start the connection
NSURL * httpsURL = [NSURL URLWithString:@"https://www.google.com"];
self.connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:httpsURL] delegate:self];
//回調
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    //1)獲取trust object
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    SecTrustResultType result;

    //2)SecTrustEvaluate對trust進行驗證
    OSStatus status = SecTrustEvaluate(trust, &result);
    if (status == errSecSuccess &&
        (result == kSecTrustResultProceed ||
        result == kSecTrustResultUnspecified)) {
        //3)驗證成功,生成NSURLCredential憑證cred,告知challenge的sender使用這個憑證來繼續連接
        NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
        [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
    } else {
        //5)驗證失敗,取消這次驗證流程
        [challenge.sender cancelAuthenticationChallenge:challenge];
  }
}

上面是代碼是通過系統默認驗證流程來驗證證書的。假如我們是自建證書的呢?這樣Trust Object里面服務器的證書因為不是可信任的CA簽發的,所以直接使用SecTrustEvaluate進行驗證是不會成功。又或者,即使服務器返回的證書是信任CA簽發的,又如何確定這證書就是我們想要的特定證書?這就需要先在本地導入證書,設置成需要參與驗證的Anchor Certificate(錨點證書,通過SecTrustSetAnchorCertificates設置了參與校驗錨點證書之后,假如驗證的數字證書是這個錨點證書的子節點,即驗證的數字證書是由錨點證書對應CA或子CA簽發的,或是該證書本身,則信任該證書),再調用SecTrustEvaluate來驗證。代碼如下:

//先導入證書
NSString * cerPath = ...; //證書的路徑
NSData * cerData = [NSData dataWithContentsOfFile:cerPath];
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));
self.trustedCertificates = @[CFBridgingRelease(certificate)];
//回調
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    //1)獲取trust object
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    SecTrustResultType result;
    //注意:這里將之前導入的證書設置成下面驗證的Trust Object的anchor certificate
    SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)self.trustedCertificates);
    //2)SecTrustEvaluate會查找前面SecTrustSetAnchorCertificates設置的證書或者系統默認提供的證書,對trust進行驗證
    OSStatus status = SecTrustEvaluate(trust, &result);
    if (status == errSecSuccess &&
        (result == kSecTrustResultProceed ||
        result == kSecTrustResultUnspecified)) {
        //3)驗證成功,生成NSURLCredential憑證cred,告知challenge的sender使用這個憑證來繼續連接
        NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];
        [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
    } else {
        //5)驗證失敗,取消這次驗證流程
        [challenge.sender cancelAuthenticationChallenge:challenge];
  }
}
建議采用本地導入證書的方式驗證證書,來保證足夠的安全性。更多的驗證方法,請查看官方文檔《HTTPS Server Trust Evaluation》
  1. 使用AFNetworking來支持HTTPS
    AFNetworking是iOS/OSX開發最流行的第三方開源庫之一,其作者是非常著名的iOS/OSX開發者Mattt Thompson,其博客NSHipster也是iOS/OSX開發者學習和開闊技術視野的好地方。AFNetworking已經將上面的邏輯代碼封裝好,甚至更完善,在AFSecurityPolicy文件中,有興趣可以閱讀這個模塊的代碼;AFNetworking上配置對HTTPS的支持非常簡單:
NSURL * url = [NSURL URLWithString:@"https://www.google.com"];
AFHTTPRequestOperationManager * requestOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:url];
dispatch_queue_t requestQueue = dispatch_create_serial_queue_for_name("kRequestCompletionQueue");
requestOperationManager.completionQueue = requestQueue;

AFSecurityPolicy * securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

//allowInvalidCertificates 是否允許無效證書(也就是自建的證書),默認為NO
//如果是需要驗證自建證書,需要設置為YES
securityPolicy.allowInvalidCertificates = YES;

//validatesDomainName 是否需要驗證域名,默認為YES;
//假如證書的域名與你請求的域名不一致,需把該項設置為NO;如設成NO的話,即服務器使用其他可信任機構頒發的證書,也可以建立連接,這個非常危險,建議打開。
//置為NO,主要用于這種情況:客戶端請求的是子域名,而證書上的是另外一個域名。因為SSL證書上的域名是獨立的,假如證書上注冊的域名是www.google.com,那么mail.google.com是無法驗證通過的;當然,有錢可以注冊通配符的域名*.google.com,但這個還是比較貴的。
//如置為NO,建議自己添加對應域名的校驗邏輯。
securityPolicy.validatesDomainName = YES;

//validatesCertificateChain 是否驗證整個證書鏈,默認為YES
//設置為YES,會將服務器返回的Trust Object上的證書鏈與本地導入的證書進行對比,這就意味著,假如你的證書鏈是這樣的:
//GeoTrust Global CA 
//    Google Internet Authority G2
//        *.google.com
//那么,除了導入*.google.com之外,還需要導入證書鏈上所有的CA證書(GeoTrust Global CA, Google Internet Authority G2);
//如是自建證書的時候,可以設置為YES,增強安全性;假如是信任的CA所簽發的證書,則建議關閉該驗證,因為整個證書鏈一一比對是完全沒有必要(請查看源代碼);
securityPolicy.validatesCertificateChain = NO;
requestOperationManager.securityPolicy = securityPolicy;
這就是AFNetworking的支持HTTPS的主要配置說明,AFHTTPSessionManager與之基本一致,就不重復了。
  1. 總結
    雖然HTTPS相比于HTTP來說,會有一定的性能上的劣勢,但對于網絡飛速發展,移動設備的性能成倍增長的今天,安全才是我們更應該去考慮的。全網HTTPS并不是那么遙遠。
下一篇準備講內存數據安全和持久化數據的安全,敬請期待。
轉載自Jaminzzhang的blog:http://oncenote.com/2014/10/21/Security-1-HTTPS/
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容

  • iOS安全系列之一:HTTPS 2014-10-21 如何打造一個安全的App?這是每一個移動開發者必須面對的問題...
    不作不會死閱讀 763評論 0 4
  • 如何打造一個安全的App?這是每一個移動開發者必須面對的問題。在移動App開發領域,開發工程師對于安全方面的考慮普...
    番薯大佬閱讀 300評論 0 0
  • 作者:Jaminzzhang 如何打造一個安全的App?這是每一個移動開發者必須面對的問題。在移動App開發領域,...
    __Lex閱讀 794評論 0 5
  • 如何打造一個安全的App?這是每一個移動開發者必須面對的問題。在移動App開發領域,開發工程師對于安全方面的考慮普...
    Crazy2015閱讀 231評論 0 0
  • 原文地址:iOS安全系列之一:HTTPS 如何打造一個安全的App?這是每一個移動開發者必須面對的問題。在移動Ap...
    violafa閱讀 872評論 0 2