#iOS應用網絡安全之HTTPShtml
##1. HTTPS/SSL的基本原理ios
安全套接字層 (Secure Socket Layer, SSL) 是用來實現互聯網安全通訊的最廣泛的標準。Web 應用程序使用 HTTPS(基於 SSL 的 HTTP),HTTPS 使用數字證書來確保在服務器和客戶端之間進行安全、加密的通訊。在 SSL 鏈接中,客戶機和服務器在發送數據以前都要對數據進行加密,而後由接受方對其進行解密。web
當瀏覽器(客戶端)須要與某個安全站點創建鏈接時,先創建TCP鏈接(三次握手),而後再發生 SSL會話握手:數據庫
握手以後,即表示客戶端已驗證了 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的以下作法:
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
站點 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那邊的出錯信息圖:
最後,關於iOS9和OSX 10.11 開發時,Xcode的Info.plist的NSAppTransportSecurity詳細設置方法請參考Apple官方文檔: NSAppTransportSecurity Reference