標籤:ios應用程式 戴維營 網路安全https socket
戴維營教育原創文章,轉載請註明出處。我們的夢想是做最好的iOS開發培訓!
iOS應用網路安全之HTTPS1. 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的如下做法:
<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傳輸的資料:
650) this.width=650;" src="http://io.diveinedu.com/images/charles_https_unsafe.jpg" />
是在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全部放開,設定資訊如下:
650) this.width=650;" src="http://io.diveinedu.com/images/xcode_info_plist_ats.png" />
<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調試終端出錯資訊圖:
650) this.width=650;" src="http://io.diveinedu.com/images/xcode_ios_https_charles_fails1.png" />
Proxy 伺服器Charles那邊的出錯資訊圖:
650) this.width=650;" src="http://io.diveinedu.com/images/xcode_ios_https_charles_fails2.png" />
最後,關於iOS9和OSX 10.11 開發時,Xcode的Info.plist的NSAppTransportSecurity詳細設定方法請參考Apple官方文檔: NSAppTransportSecurity Reference
戴維營學院(進階開發視頻): http://v.diveinedu.com
潛心俱樂部(iOS面試必備): http://divein.club
Ios應用網路安全之https