[轉]iOS應用網絡安全之HTTPS

原文鏈接:http://my.oschina.net/kgdugyiy/blog/600367

1. HTTPS/SSL的基本原理

安全套接字層 (Secure Socket Layer, SSL) 是用來實現互聯網安全通信的最普遍的標準。Web 應用程序使用 HTTPS(基于 SSL 的 HTTP),HTTPS 使用數字證書來確保在服務器和客戶端之間進行安全、加密的通信。在 SSL 連接中,客戶機和服務器在發送數據之前都要對數據進行加密,然后由接受方對其進行解密。
當瀏覽器(客戶端)需要與某個安全站點建立連接時,先建立TCP連接(三次握手),然后再發生 SSL會話握手:

  • 瀏覽器將通過網絡發送請求安全會話的消息(通常請求以 https 而非 http 開頭的 URL)。
  • 服務器通過發送其證書(包括公鑰)進行響應。
  • 瀏覽器將檢驗服務器的證書是否有效,并檢驗該證書是否是由其證書位于瀏覽器的數據庫中的(并且是可信的)CA 所簽發的。它還將檢驗 CA 證書是否已過期。
  • 如果證書有效,瀏覽器將生成一個--一次性的、唯一的--會話密鑰,并使用服務器的公鑰對該會話密鑰進行加密。然后,瀏覽器將把加密的會話密鑰發送給服務器,這樣服務器和瀏覽器都有一份會話密鑰。
  • 服務器可以使用其專用密鑰對消息進行解密,然后恢復會話密鑰。

握手之后,即表示客戶端已驗證了 Web 站點的身份,并且只有該客戶端和 Web 服務器擁有會話密鑰副本。從現在開始,客戶機和服務器便可以使用該會話密鑰對彼此間的所有通信進行加密。這樣就確保了客戶機和服務器之間的通信的安全性。
上面是一般也是應用最普遍的單向驗證方式,由瀏覽器(客戶端)來驗證服務端的合法性;其實也可以做雙向驗證,服務器也可以驗證瀏覽器(客戶端)的合法性,不過一般使用在銀行業務上,比如U盾之類。我們現在關注普遍的單向驗證方式的應用。

2. iOS移動開發HTTPS應用現狀

當下絕大部份的移動互聯網項目都采用HTTP、HTTPS協議作為前后端的數據接口協議。而iOS開發群體中,絕大部分都在項目中應用了第三方開源的HTTP請求框架AFNetworking來快速而高效的開發。AFNetworking請求HTTP接口簡直是簡單得不能再簡單了。只不過從iOS9.0開始需要設置Info.plist中App Transport Security打開非HTTP的資源加載,因為Apple默認只允許采用經過權威證書頒發機構簽名的證書的HTTPS站點的訪問,一切是為了安全。安全。安全。
那么我們重點來分析采用HTTPS協議的后臺接口的一般使用方式: HTTPS的服務器配置的證書分兩大類,一類是經過權威機構簽名頒發的證書,這樣證書通常是要花錢買服務的,而且不便宜,當然現在也有少數機構提供免費的證書簽名服務。另一類就是服務器配置的是研發人員自己簽名生成的證書。

3.AFN調用使用權威機構頒發證書的HTTPS接口

現在AFNetworking框架已經修復了之前爆出的SSL中間人攻擊漏洞,并強烈要求開發者使用公鑰綁定或者證書綁定的安全策略,那么正確使用AFNetworking請求這類證書的HTTPS站點代碼很簡單如下:

AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey]; policy.validatesDomainName = YES; AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; manager.securityPolicy = policy; manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;

對于這類證書的站點,Info.plist都不需要設置,因為已經是權威機構頒發的證書了,我們只需要設置驗證綁定方式和驗證域名以防止中間人攻擊,畢竟申請證書是花了錢(現在也有免費的申請,比如WoSign),省事一點。

4.AFN調用使用我們自己簽名證書的HTTPS接口

對于使用我們自己簽名的證書來說,瀏覽器打開web站點也會默認阻止訪問,除非用戶手動把該站點加入信任列表,這個手動加入的過程其實就是不去驗證服務器的合法性,任性的認為服務器是可信賴的。 那么手動加入信任列表,這樣會導致證書的驗證過程壓根沒發生,雖然可以成功訪問目標服務器返回我們需要的數據,其實,這中間很有可能返回的數據不是正真的目標服務器返回的數據,也可能是網絡傳輸中間的第三者偽裝返回的數據。傳輸的數據被人竊取甚至纂改都是很可能的。

4.1 不正確的做法

瀏覽器手動加入自簽名站點到信任列表這個操作的功能相當于iOS開發中AFNetworking的API的如下做法:
A 非權威機構頒發證書的HTTPS請求一樣必須先在Info.plist設置如下:

<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

B AFNetworking代碼設置SecurityPolicy
站點 https://tv.diveinedu.com 是我前面博客所講的配置方法配置的自簽名證書。
此處是博客鏈接

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; //允許非權威機構頒發的證書 manager.securityPolicy.allowInvalidCertificates = YES; //也不驗證域名一致性 manager.securityPolicy.validatesDomainName = NO; //關閉緩存避免干擾測試 manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData; [manager GET:@"https://tv.diveinedu.com/channel/" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { NSLog(@"%@",responseObject); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"%@",error); }];

經過如上兩步設置之后,我們可以在iOS應用中訪問我們采用自簽名證書的HTTPS站點了。但是這個是不安全的,因為他在沒有使用HTTPS/SSL代理和使用像Charles那樣的HTTPS/SSL代理的情況下都可以訪問服務器資源. 完全可以說是白費功夫,只能防止“君子”在網絡中用Wireshark之類來TCP抓包嗅探。因為畢竟還是HTTPS加密了傳輸數據了。那為什么我要說這樣是白費功夫呢,因為這個辦法不能防止中間人攻擊!比如用戶可以給手機設置HTTPS的SSL代理(比如Charles),完全可以在代理中看到明文數據,所以,既然用了HTTPS就要防止中間人攻擊,不然還不如不用HTTPS。
下面我們來看看怎么用Charles代理抓包工具所抓到的HTTPS傳輸的數據:


上圖是在Mac上運行Charles工具代理抓包,真機和Mac電腦同一個局域網,并設置代理為Mac機的IP和Charles的代理端口8888,然后啟動App請求網絡后抓到的數據。是不是很意外啊。HTTPS的數據也抓出明文了。 顯然這樣是非常不安全的,那么當我們使用自簽名證書的時候,我們該如何來在App端(客戶端)嚴格的驗證服務器的合法性呢?

4.2 正確的做法

我們要在App端嚴格驗證服務器的合法性,防止網絡中間的代理或者防火墻進行中間人的攻擊和證書欺騙,那么我們需要把服務器配置的證書打包到客戶端程序中(私鑰留服務器不要分發不用泄露,非常重要),在代碼里去讀取該證書/公鑰信息和服務器返回的進行匹配驗證. 在iOS開發中,從Xcode7和iOS9開始,Apple提升了App的網絡安全性,App默認只能進行對采用權威機構簽名頒發證書的Web站點進行訪問(信任的HTTPS),而自簽名的證書的HTTPS站點也被列為屬于例外,所以我們需要在App的 Info.plist 中單獨為我們的域名設置 Exception Domains "白名單",而不是打開 Allow Arbitrary Loads 全部放開,設置信息如下:

<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>tv.diveinedu.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>

這樣就不像上面那個方法那樣一刀切全部放開, 而是單獨為某個域名放開設置.當然上面也可以使用放開全部的設置NSAllowsArbitraryLoads為true.但是我建議使用白名單.
除此之外,要做到嚴格驗證防止像Charles那樣的中間人代理抓包,AFNetworking代碼應該用如下設置:

//服務器端配置的包含公鑰的證書分發到客戶端后,需要轉換為DER格式的證書文件. //openssl x509 -outform der -in tv.diveinedu.com.crt -out tv.diveinedu.com.der NSString *certFilePath = [[NSBundle mainBundle] pathForResource:@"tv.diveinedu.com" ofType:@"der"]; NSData *certData = [NSData dataWithContentsOfFile:certFilePath]; NSSet *certSet = [NSSet setWithObject:certData]; AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey withPinnedCertificates:certSet]; policy.allowInvalidCertificates = YES; AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; manager.securityPolicy = policy; //關閉緩存避免干擾測試 manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData; [manager GET:@"https://tv.diveinedu.com/channel/" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { NSLog(@"%@",responseObject); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"%@",error); }];

上面的代碼能夠驗證服務器身份在沒有使用代理的時候可以正常訪問服務器的資源,但是一旦用戶給手機網絡設置使用了如Charle那樣的HTTPS/SSL代理服務,則會出現服務器證書驗證失敗,SSL網絡連接會斷開,老板再也不用擔心數據接口被人抓包或者代理給扒出來了.故達到防止中間人攻擊的效果.
當使用Charles SSL代理時Xcode調試終端出錯信息圖:


代理服務器Charles那邊的出錯信息圖:
Charles那邊的出錯信息
Charles那邊的出錯信息

最后,關于iOS9和OSX 10.11 開發時,Xcode的Info.plist的NSAppTransportSecurity詳細設置方法請參考Apple官方文檔: NSAppTransportSecurity Reference

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 原文地址 http://blog.csdn.net/u012409247/article/details/4985...
    0fbf551ff6fb閱讀 3,582評論 0 13
  • 一、作用 不使用SSL/TLS的HTTP通信,就是不加密的通信。所有信息明文傳播,帶來了三大風險。 (1)竊聽風險...
    XLsn0w閱讀 10,746評論 2 44
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,991評論 19 139
  • 移動互聯網開發中iOS應用的網絡安全問題往往被大部分開發者忽略,iOS9和OS X 10.11開始Apple也默認...
    戴維營教育閱讀 13,554評論 11 79
  • 111
    家榮111閱讀 349評論 0 0