Ios應用網絡安全之https

戴維營教育原創文章,轉載請註明出處。我們的夢想是做最好的iOS開發培訓!

iOS應用網絡安全之HTTPS

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>

這樣就不像上面那個方法那樣一刀切全部放開, 而是單獨爲某個域名放開設置.當然上面也可以使用放開全部的設置NSAllowsArbitraryLoadstrue.但是我建議使用白名單.

除此之外,要做到嚴格驗證防止像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

戴維營學院(高級開發視頻): http://v.diveinedu.com

潛心俱樂部(iOS面試必備): http://divein.club


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章