iOS安全系列之二:HTTPS進階

上一篇《iOS安全系列之一:HTTPS》被CocoaChina轉(zhuǎn)載,還順便上了下頭條: 打造安全的App瑟慈!iOS安全系列之 HTTPS炒刁,但那篇文章只是介紹了比較偏應(yīng)用的初級知識,對于想要深入了解HTTPS的同學(xué)來說是遠遠不夠的水醋,剛好本人最近工作上也遇到并解決了一些HTTPS相關(guān)的問題,以此為契機彪置,決定寫這篇更深入介紹HTTPS的文章拄踪。

本文分為以下五節(jié):

  1. 中間人攻擊:介紹中間人攻擊常見方法,并模擬了一個簡單的中間人攻擊拳魁;
  2. 校驗證書的正確姿勢:介紹校驗證書的一些誤區(qū)惶桐,并討論了正確校驗方式;
  3. ATS:討論下 iOS 9.0 新發(fā)布的的特性App Transport Security
  4. 調(diào)試SSL/TLS:討論使用Wireshark進行SSL/TLS調(diào)試的方法姚糊;
  5. 后記

其中第1節(jié)“中間人”是比較常見基礎(chǔ)的知識贿衍,網(wǎng)上也可以找到相關(guān)的資料,如果對中間人攻擊已經(jīng)有了足夠的了解叛拷,可以跳過舌厨。后面幾節(jié)則是個人在iOS方面的實踐總結(jié),除了一些與系統(tǒng)相關(guān)的特性外忿薇,大部分都是系統(tǒng)無關(guān)的通用知識裙椭,并且每一章節(jié)都比較獨立,所以可以直接跳到感興趣的地方閱讀署浩。

1. 中間人攻擊

關(guān)于HTTPS揉燃,我經(jīng)常會提到的就是中間人攻擊,那究竟什么是中間人攻擊呢筋栋?中間人攻擊炊汤,即所謂的Man-in-the-middle attack(MITM),顧名思義弊攘,就是攻擊者插入到原本直接通信的雙方抢腐,讓雙方以為還在直接跟對方通訊,但實際上雙方的通信對方已變成了中間人襟交,信息已經(jīng)是被中間人獲取或篡改迈倍。

man_in_the_middle.png

當然,本文并不是科普性文章捣域,本節(jié)就針對HTTPS攻擊啼染,特別是HTTPS在App這一應(yīng)用場景下的常見的攻擊手段進行分析討論。

由前文我們知道焕梅,HTTPS在建立了TCP連接之后迹鹅,會進行SSL握手(SSL Handshake)來校驗證書,協(xié)商加密協(xié)議和對稱加密的密鑰贞言,之后就會使用協(xié)商好的密鑰來進行傳輸斜棚。所以HTTPS攻擊一般分為SSL連接建立前的攻擊,以及HTTPS傳輸過程中的攻擊该窗;

常見的HTTPS中間人攻擊打肝,首先需要結(jié)合ARP、DNS欺騙等技術(shù)挪捕,來對會話進行攔截粗梭,

1.1 SSL證書欺騙攻擊

此類攻擊較為簡單常見。首先通過ARP欺騙级零、DNS劫持甚至網(wǎng)關(guān)劫持等等断医,將客戶端的訪問重定向到攻擊者的機器滞乙,讓客戶端機器與攻擊者機器建立HTTPS連接(使用偽造證書),而攻擊者機器再跟服務(wù)端連接鉴嗤。這樣用戶在客戶端看到的是相同域名的網(wǎng)站斩启,但瀏覽器會提示證書不可信,用戶不點擊繼續(xù)瀏覽就能避免被劫持的醉锅。所以這是最簡單的攻擊方式兔簇,也是最容易識別的攻擊方式。

此類攻擊有個經(jīng)典的工具:SSLSniff硬耍。SSLSniff是大神Moxie Marlinspike開發(fā)的工具垄琐,該工具一開始是設(shè)計用于上一篇文章中提到的Basic Constaints 漏洞的,這類系統(tǒng)級別的漏洞经柴,基本上可以讓你不知不覺狸窘;現(xiàn)在的操作系統(tǒng)和瀏覽器基本修復(fù)了這一漏洞。但也可以使用SSLSniff來偽造證書實現(xiàn)釣魚攻擊坯认。

mitm-sniff.png

防范措施:

釣魚類攻擊翻擒,App直接調(diào)用系統(tǒng)API創(chuàng)建的HTTPS連接(NSURLConnection)一般不會受到影響,只使用默認的系統(tǒng)校驗牛哺,只要系統(tǒng)之前沒有信任相關(guān)的偽造證書陋气,校驗就直接失敗,不會SSL握手成功引润;但如果是使用WebView瀏覽網(wǎng)頁恩伺,需要在UIWebView中加入較強的授權(quán)校驗,禁止用戶在校驗失敗的情況下繼續(xù)訪問椰拒。

1.2 SSL剝離攻擊(SSLStrip

SSL剝離,即將HTTPS連接降級到HTTP連接凰荚。假如客戶端直接訪問HTTPS的URL燃观,攻擊者是沒辦法直接進行降級的,因為HTTPS與HTTP雖然都是TCP連接便瑟,但HTTPS在傳輸HTTP數(shù)據(jù)之前缆毁,需要在進行了SSL握手,并協(xié)商傳輸密鑰用來后續(xù)的加密傳輸到涂;假如客戶端與攻擊者進行SSL握手脊框,而攻擊者無法提供可信任的證書來讓客戶端驗證通過進行連接,所以客戶端的系統(tǒng)會判斷為SSL握手失敗践啄,斷開連接浇雹。

該攻擊方式主要是利用用戶并不會每次都直接在瀏覽器上輸入https://xxx.xxx.com來訪問網(wǎng)站,或者有些網(wǎng)站并非全網(wǎng)HTTPS屿讽,而是只在需要進行敏感數(shù)據(jù)傳輸時才使用HTTPS的漏洞昭灵。中間人攻擊者在劫持了客戶端與服務(wù)端的HTTP會話后,將HTTP頁面里面所有的https://超鏈接都換成http://,用戶在點擊相應(yīng)的鏈接時烂完,是使用HTTP協(xié)議來進行訪問试疙;這樣,就算服務(wù)器對相應(yīng)的URL只支持HTTPS鏈接抠蚣,但中間人一樣可以和服務(wù)建立HTTPS連接之后祝旷,將數(shù)據(jù)使用HTTP協(xié)議轉(zhuǎn)發(fā)給客戶端,實現(xiàn)會話劫持嘶窄。

這種攻擊手段更讓人難以提防怀跛,因為它使用HTTP,不會讓瀏覽器出現(xiàn)HTTPS證書不可信的警告护侮,而且用戶很少會去看瀏覽器上的URL是https://還是http://敌完。特別是App的WebView中,應(yīng)用一般會把URL隱藏掉羊初,用戶根本無法直接查看到URL出現(xiàn)異常滨溉。

mitm-sslstrip.png

防范措施:

該種攻擊方式同樣無法劫持App內(nèi)的HTTPS連接會話,因為App中傳入請求的URL參數(shù)是固定帶有https://的长赞;但在WebView中打開網(wǎng)頁同樣需要注意晦攒,在非全網(wǎng)HTTPS的網(wǎng)站,建議對WebView中打開的URL做檢查得哆,檢查應(yīng)該使用https://的URL是否被篡改為http://脯颜;也建議服務(wù)端在配置HTTPS服務(wù)時,加上“HTTP Strict Transport Security”配置項贩据。

參考:【流量劫持】躲避HSTS的HTTPS劫持

1.3 針對SSL算法進行攻擊

上述兩種方式栋操,技術(shù)含量較低,而且一般只能影響 WebApp饱亮,而很難攻擊到 Native App 矾芙, 所以高階的 Hacker,會直接針對SSL算法相關(guān)漏洞進行攻擊近上,期間會使用很多的密碼學(xué)相關(guān)手段剔宪。由于本人非專業(yè)安全相關(guān)人員,沒有多少相關(guān)實踐經(jīng)驗壹无,所以本節(jié)不會深入講解相關(guān)的攻擊原理和手段葱绒,有興趣的同學(xué)可以查看以下拓展閱讀:

防范措施:

這類攻擊手段是利用SSL算法的相關(guān)漏洞,所以最好的防范措施就是對服務(wù)端 SSL/TLS 的配置進行升級:

  • 只支持盡量高版本的TLS(最低TLSv1)斗锭;
  • 禁用一些已爆出安全隱患的加密方法地淀;
  • 使用2048位的數(shù)字證書;

1.4 模擬最簡單的攻擊

經(jīng)過上述幾種攻擊方式的說明之后岖是,我們來模擬下最簡單的中間人攻擊骚秦。

中間人攻擊步驟方式的上文已經(jīng)說過了她倘,流量劫持相關(guān)操作不是本文重點,可以參考流量劫持是如何產(chǎn)生的作箍?硬梁, 本例直接使用Charles來做代理,對流量進行劫持胞得。并使用SSL代理來模擬下對iPhone設(shè)備HTTPS請求的中間人攻擊荧止,讓大家在思考理解中間人攻擊方式的同時,了解在開發(fā)中如何防范類似的攻擊阶剑。

1) Charles設(shè)置代理

在Charles中開啟并設(shè)置HTTP代理和SSL代理跃巡,Menu -> Proxy -> Proxy Setting,設(shè)置如圖:

HTTP代理設(shè)置牧愁,注意記住端口號為:8888

charles-http-proxy.jpg

SSL代理設(shè)置素邪,在Locations上可以設(shè)置想要進行SSL代理的域名,這里以百度的百付寶*.baifubao.com為模擬對象猪半。

charles-ssl-proxy.jpg

2) 在iPhone端設(shè)置HTTP代理

在Mac上獲取當前機器的IP地址:

ifconfig en0:

ifconfig.jpg

還有一個簡單的方法兔朦,按住option+點擊頂部菜單欄的WiFi網(wǎng)絡(luò)圖標:

option-wifi.jpg

可以看到當前電腦的IP地址為:192.168.199.249

將iPhone連接到與電腦相同的WiFi磨确,在iPhone設(shè)置中:無線局域網(wǎng) -> 已連接WiFi右邊的Info詳情圖標 -> HTTP代理 -> 手動 -> 設(shè)置HTTP代理:

iphone-http-proxy.jpg

設(shè)置完成之后沽甥,打開Safari隨便訪問一個網(wǎng)頁,初次設(shè)置代理的話乏奥,Charles會彈出一個iPhone請求代理的確認框摆舟,點擊Allow即可。然后在Charles上就可以看到iPhone上的HTTP請求了邓了。為了避免Mac上的請求過多影響對被代理iPhone上HTTP請求的查看和調(diào)試恨诱,可以在Charles取消Mac的代理:Menu -> Proxy -> 取消勾選Mac OS X Proxy 即可。

假如你訪問的是被代理的目標 URL http://www.baifubao.com 則打不開網(wǎng)頁骗炉。因為iPhone的HTTPS請求已經(jīng)被Charles攔截照宝,但iPhone無法信任Charles的證書,所以SSL Handshake失敗痕鳍,無法建立HTTPS連接。

charles-ssl-handshake-failed.jpg

3) 偽造證書欺騙

在被代理的iPhone上打開Safari龙巨,訪問http://www.charlesproxy.com/getssl笼呆,會彈出安裝描述符文件的界面,該描述文件包含了Charles根證書:

charles-root-cer.jpg

注意:這個Charles證書是內(nèi)置在Charles中的旨别,可以在菜單Help -> SSL Proxying可以直接保存和安裝證書诗赌。安裝后的描述文件可以在iPhone設(shè)備的設(shè)置 -> 通用 -> 描述文件進行查看和管理。

“安裝”完成之后秸弛,就會將Charles根證書加入系統(tǒng)可信任證書列表中铭若,使用該證書簽發(fā)的子證書也會被系統(tǒng)信任洪碳。Charles會為之前SSL代理設(shè)置中配置的域名生成對應(yīng)的SSL證書,這樣偽造證書的證書就實現(xiàn)了欺騙叼屠⊥纾可以使用Mac SSL代理查看下:

baidu-cer.jpg

4) 結(jié)果驗證

下載百度App,然后登錄賬號镜雨,在我 -> 我的錢包嫂侍,就會訪問百付寶:

baifubao-ssl-proxy-success.jpg

看到已成功獲取到HTTPS請求包的內(nèi)容。從這里荚坞,我們可以猜測出該App是使用系統(tǒng)默認的校驗方式:系統(tǒng)信任了這個中間人服務(wù)器返回的SSL證書挑宠,App就信任了這一校驗,SSL握手成功颓影;而沒有對服務(wù)器證書進行本地對比校驗各淀。這是當下非常多App存在的安全隱患。

這個簡單的SSL代理模擬了簡單釣魚式的中間人攻擊诡挂,大家應(yīng)該都基本明白了這種攻擊方式的所針對的漏洞碎浇,以及防范這種攻擊方法的措施:

  • 不要隨意連入公共場合內(nèi)的WiFi,或者使用未知代理服務(wù)器
  • 不要安裝不可信或突然出現(xiàn)的描述文件咆畏,信任偽造的證書南捂;
  • App內(nèi)部需對服務(wù)器證書進行單獨的對比校驗,確認證書不是偽造的旧找;

2. 校驗證書的正確姿勢

上一節(jié)對中間人攻擊進行了簡單介紹溺健,本節(jié)就上一節(jié)我們遇到的安全隱患問題,來討論下在App中钮蛛,應(yīng)該怎么校驗服務(wù)器返回的SSL證書鞭缭,來保證HTTPS通信的安全。上一篇文章《iOS安全系列之一:HTTPS》有對基本校驗過程相關(guān)代碼進行講解魏颓,本文不會贅述這些細節(jié)岭辣,而是主要討論校驗證書中幾個重要的點:

2.1 域名驗證

前不久,iOS上最知名的網(wǎng)絡(luò)開源庫AFNetworking爆出HTTPS校驗漏洞甸饱,該漏洞是因為其校驗策略模塊 AFSecurityPolicy 內(nèi)的參數(shù) validatesDomainName 默認為NO沦童,這會導(dǎo)致校驗證書的時候不會校驗這個證書對應(yīng)的域名。即請求返回的服務(wù)器證書叹话,只要是可信任CA機構(gòu)簽發(fā)的偷遗,都會校驗通過,這是非常嚴重的漏洞驼壶。該漏洞已在v2.5.2版本中修復(fù)氏豌,對應(yīng)Git版本號3e631b203dd95bb82dfbcc2c47a2d84b59d1eeb4

這個漏洞以及AFNetworking的相關(guān)源碼會讓很多人以為系統(tǒng)的默認校驗是不校驗證書對應(yīng)域名的热凹,實際上并非如此泵喘。這里AFNetworking確有畫蛇添足之嫌泪电。首先我們查看下系統(tǒng)的默認校驗策略:

<figure class="highlight" style="box-sizing: border-box; color: rgb(0, 0, 0); font-family: "PingFang SC", "Source Han Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 1px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;">

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    //1)獲取trust object
    SecTrustRef trust = challenge.protectionSpace.serverTrust;

    //獲取默認的校驗策略
    CFArrayRef defaultPolicies = NULL;
    SecTrustCopyPolicies(serverTrust, &defaultPolicies);
    NSLog(@"Default Trust Policies: %@", (__bridge id)defaultPolicies);

    //...
}

</figure>

打印默認校驗策略信息:

    5 : <CFString 0x197814dc0 [0x196ea5fa0]>{contents = "ValidRoot"} = <CFBoolean 0x196ea6340 [0x196ea5fa0]>{value = true}
    6 : <CFString 0x197814b20 [0x196ea5fa0]>{contents = "SSLHostname"} = <CFString 0x170226b60 [0x196ea5fa0]>{contents = "xxx.xxx.com"}
    8 : <CFString 0x197814da0 [0x196ea5fa0]>{contents = "ValidLeaf"} = <CFBoolean 0x196ea6340 [0x196ea5fa0]>{value = true}

從打印信息來看,系統(tǒng)的默認校驗策略中已包含了域名校驗纪铺。然后再看AFSecurityPolicy中相關(guān)源碼:

<figure class="highlight" style="box-sizing: border-box; color: rgb(0, 0, 0); font-family: "PingFang SC", "Source Han Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 1px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;">

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain:(NSString *)domain
{
    NSMutableArray *policies = [NSMutableArray array];
    if (self.validatesDomainName) {
        [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
    } else {
        [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
    }

    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);

    //...
}

</figure>

這其實也是很多開發(fā)者在處理異常與默認邏輯分支時會犯的錯誤相速,這段邏輯推薦實現(xiàn)方式是:

<figure class="highlight" style="box-sizing: border-box; color: rgb(0, 0, 0); font-family: "PingFang SC", "Source Han Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 1px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;">

//取代validatesDomainName,默認為NO霹陡,就是系統(tǒng)默認行為
@property (nonatomic, assign) BOOL skipDomainNameValidation;

//校驗
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain:(NSString *)domain
{
    if (self.skipDomainNameValidation) {
        NSMutableArray *policies = [NSMutableArray array];
        [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
        SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
    }

    //...
}

</figure>

從代碼上看和蚪,邏輯是否變得更清晰了?而且也表明系統(tǒng)默認的校驗方式是會驗證域名的烹棉。實際上調(diào)用SecTrustSetPolicies來重新設(shè)置校驗策略攒霹,主要是用于使用IP進行HTTPS請求,或者一個證書用于多個域名的場景浆洗;在這些場景下催束,服務(wù)器證書上的域名和請求域名(可能是IP,也可能是其他域名)就會出現(xiàn)不一致伏社,導(dǎo)致校驗不通過抠刺;這就需要重新設(shè)置下校驗策略,把這個證書對應(yīng)的域名設(shè)置下摘昌。詳細說明請查看官方文檔:《Overriding TLS Chain Validation Correctly》

2.2 校驗證書鏈速妖?

上一篇文章介紹系統(tǒng)驗證SSL證書的方法和流程時,不是已經(jīng)說明了會對證書鏈進行層層校驗聪黎,以保證證書的可信么罕容?為什么還需要討論這一問題?其實本節(jié)要討論的是AFNetworkingvalidatesCertificateChain的問題稿饰。

先說明下結(jié)果:在AFNetworking最新發(fā)布的V2.6.0锦秒,已經(jīng)將該特性去掉了。相關(guān)的討論:SSL Pinning: What Should Be Certificate Chain Validation Expected Behavior?#2744

AFNetworking中實現(xiàn)的驗證證書鏈喉镰,是將App本地打包好的證書與服務(wù)器返回的證書鏈進行數(shù)據(jù)上的一一對比旅择,只有打包到App的證書中包含了服務(wù)器返回的證書鏈上的所有證書,校驗才會通過侣姆。如google的SSL證書:

google-cer.png

開啟validatesCertificateChain后請求https://google.com生真,需要將GeoTrust Global CA、Google Internet Authority G2和google.com的證書都導(dǎo)入App中才能驗證通過捺宗。請回憶下上一篇文章關(guān)于證書鏈的可信任機制柱蟀,會發(fā)現(xiàn)這是完全沒有必要的;證書鏈的驗證偿凭,主要由三部分來保證證書的可信:葉子證書是對應(yīng)HTTPS請求域名的證書产弹,根證書是被系統(tǒng)信任的證書派歌,以及這個證書鏈之間都是層層簽發(fā)可信任鏈弯囊;證書之所以能成立痰哨,本質(zhì)是基于信任鏈,這樣任何一個節(jié)點證書加上域名校驗(CA機構(gòu)不會為不同的對不同的用戶簽發(fā)相同域名的證書)匾嘱,就確定一條唯一可信證書鏈斤斧,所以不需要每個節(jié)點都驗證。

2.3打包證書校驗

那是否就不需要在App中打包證書進行驗證了呢霎烙?

這時需要想想為什么偽造證書是可以實現(xiàn)中間人攻擊的撬讽?答案就在于用戶讓系統(tǒng)信任了不應(yīng)該信任的證書。用戶設(shè)置系統(tǒng)信任的證書悬垃,會作為錨點證書(Anchor Certificate)來驗證其他證書游昼,當返回的服務(wù)器證書是錨點證書或者是基于該證書簽發(fā)的證書(可以是多個層級)都會被信任。這就是基于信任鏈校驗方式的最大弱點尝蠕。我們不能完全相信系統(tǒng)的校驗烘豌,因為系統(tǒng)的校驗依賴的證書的源很可能被污染了。這就需要選取一個節(jié)點證書看彼,打包到App中廊佩,作為Anchor Certificate來保證證書鏈的唯一性和可信性。

所以還是需要App本地打包證書靖榕,使用SecTrustSetAnchorCertificates(SecTrustRef trust, CFArrayRef anchorCertificates)來設(shè)置Anchor Certificate進行校驗标锄。需要注意的是,官方文檔《Certificate, Key, and Trust Services Reference》針對傳入的 Anchor Certificates 有說明:

IMPORTANT

Calling this function without also calling SecTrustSetAnchorCertificatesOnly disables the trusting of any anchors other than the ones specified by this function call.

也就是說茁计,單純調(diào)用SecTrustSetAnchorCertificates方法后不調(diào)用SecTrustSetAnchorCertificatesOnly來驗證證書料皇,則只會相信SecTrustSetAnchorCertificates傳入的證書,而不會信任其他錨點證書簸淀。關(guān)于這一點瓶蝴,SecTrustSetAnchorCertificatesOnly方法參數(shù)講解中也有說明:

anchorCertificatesOnly:

If true, disables trusting any anchors other than the ones passed in with the SecTrustSetAnchorCertificates function. If false, the built-in anchor certificates are also trusted. If SecTrustSetAnchorCertificates is called and SecTrustSetAnchorCertificatesOnly is not called, only the anchors explicitly passed in are trusted.

只相信傳入的錨點證書,也就只會驗證通過由這些錨點證書簽發(fā)的證書租幕。這樣就算被驗證的證書是由系統(tǒng)其他信任的錨點證書簽發(fā)的舷手,也無法驗證通過。

最后一個問題:選擇證書鏈的哪一節(jié)點作為錨點證書打包到App中劲绪?很多開發(fā)者會直接選擇葉子證書男窟。其實對于自建證書來說,選擇哪一節(jié)點都是可行的贾富。而對于由CA頒發(fā)的證書歉眷,則建議導(dǎo)入頒發(fā)該證書的CA機構(gòu)證書或者是更上一級CA機構(gòu)的證書,甚至可以是根證書颤枪。這是因為:

  1. 一般葉子證書的有效期都比較短汗捡,Google和Baidu官網(wǎng)證書的有效期也就幾個月;而App由于是客戶端,需要一定的向后兼容扇住,稍疏于檢查春缕,今天發(fā)布,過兩天證書就過期了艘蹋。

  2. 越往證書鏈的末端锄贼,證書越有可能變動;比如葉子證書由特定域名(aaa.bbb.com)改為通配域名(*.bbb.com)等等女阀。短期內(nèi)的變動宅荤,重新部署后,有可能舊版本App更新不及時而出現(xiàn)無法訪問的問題浸策。

因此使用CA機構(gòu)證書是比較合適的冯键,至于哪一級CA機構(gòu)證書,并沒有完全的定論庸汗,你可以自己評估選擇琼了。

3. ATS

在本文發(fā)表的時間(2015-09-03),大部分的iOS開發(fā)同學(xué)應(yīng)該升級到iOS9了夫晌,在iOS9下進行HTTP/HTTPS請求時會遇到如下錯誤:

Request failed: Error Domain=NSURLErrorDomain Code=-1022 “The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.” UserInfo=0x7fbb4a158f00 {NSUnderlyingError=0x7fbb4a1141c0 “The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.”, NSErrorFailingURLStringKey=http://api.xxx.com/mobile, NSErrorFailingURLKey=http://api.xxx.com/mobile, NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.}

這是iOS9中一個重大的更新:App Transport Security雕薪,簡稱ATS。ATS對使用NSURLConnection, CFURL, 或NSURLSession 等 APIs 進行網(wǎng)絡(luò)請求的行為作了一系列的強制要求晓淀,反逼服務(wù)器配置所袁,以提高網(wǎng)絡(luò)數(shù)據(jù)傳輸?shù)陌踩裕?/p>

These are the App Transport Security requirements:

  1. The server must support at least Transport Layer Security (TLS) protocol version 1.2.
  1. Connection ciphers are limited to those that provide forward secrecy (see the list of ciphers below.)
  1. Certificates must be signed using a SHA256 or better signature hash algorithm, with either a 2048 bit or greater RSA key or a 256 bit or greater Elliptic-Curve (ECC) key. Invalid certificates result in a hard failure and no connection.

ATS要求運行在iOS9的App,需將HTTP連接升級到HTTPS凶掰,并且TLS版本不得低于v1.2燥爷;而且規(guī)定了支持的加密套件(Cipher Suite)和證書簽名的哈希算法;如果想要向前兼容的話懦窘,可以通過設(shè)置Info.plist來降低校驗強度前翎,具體可以看這篇文章:Configuring App Transport Security Exceptions in iOS 9 and OSX 10.11

本人升級到iOS9 GM版畅涂,從App Store上下載了一些并沒有完全支持ATS的應(yīng)用港华,使用起來也完全沒有問題,估計iOS系統(tǒng)對使用低于SDK9編譯的App做了兼容午衰,這方面也是符合預(yù)期的立宜,畢竟ATS的影響實在太大,基本上沒有任何的App能夠幸免臊岸,比如圖片下載一般使用HTTP橙数,而不會使用HTTPS。所以建議可以暫時使用NSAllowsArbitraryLoads來取消ATS的限制帅戒,后續(xù)慢慢完善對ATS的支持灯帮。

日益復(fù)雜脆弱的網(wǎng)絡(luò)難以保證用戶的數(shù)據(jù)安全,因此Apple才在iOS9上強推ATS,反向逼迫服務(wù)端升級钟哥,以提供更安全的網(wǎng)絡(luò)環(huán)境响疚。建議開發(fā)者不要簡單地將ATS禁用,而應(yīng)該升級服務(wù)器的配置支持ATS瞪醋,為用戶提供更安全的服務(wù)。

4. 調(diào)試SSL/TLS

開發(fā)一個新的App装诡,通常終端和后端先協(xié)商好了具體業(yè)務(wù)邏輯的通信協(xié)議银受,后端和終端按照協(xié)議實現(xiàn)邏輯之后,就進入聯(lián)調(diào)階段鸦采,第一次聯(lián)調(diào)往往會回到很多問題宾巍,包括數(shù)據(jù)格式不對,缺少基礎(chǔ)字段等渔伯;假如是基于HTTPS的網(wǎng)絡(luò)請求顶霞,則很可能由于后臺配置問題,導(dǎo)致遇到如CFNetwork SSLHandshake failed (-9824)這類握手失敗的錯誤锣吼。面對這類SSL錯誤选浑,該如何來解決呢?根據(jù)本人經(jīng)驗玄叠,主要是分兩步:

4.1 錯誤碼

這會不會太簡單了古徒?其實最簡單的往往是最有效的。SSL相關(guān)錯誤碼可以在<Security/SecureTransport.h>中找到读恃。上面-9824的錯誤百框,對應(yīng)的是errSSLPeerHandshakeFail = -9824, /* handshake failure */肖粮,其他常見的錯誤碼還有:

<figure class="highlight" style="box-sizing: border-box; color: rgb(0, 0, 0); font-family: "PingFang SC", "Source Han Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 1px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;">

    //...

    /* fatal errors detected by peer */
    errSSLPeerUnexpectedMsg     = -9819,    /* unexpected message received */
    errSSLPeerBadRecordMac      = -9820,    /* bad MAC */
    errSSLPeerDecryptionFail    = -9821,    /* decryption failed */
    errSSLPeerRecordOverflow    = -9822,    /* record overflow */
    errSSLPeerDecompressFail    = -9823,    /* decompression failure */
    errSSLPeerHandshakeFail     = -9824,    /* handshake failure */
    errSSLPeerBadCert           = -9825,    /* misc. bad certificate */
    errSSLPeerUnsupportedCert   = -9826,    /* bad unsupported cert format */
    errSSLPeerCertRevoked       = -9827,    /* certificate revoked */
    errSSLPeerCertExpired       = -9828,    /* certificate expired */
    errSSLPeerCertUnknown       = -9829,    /* unknown certificate */
    errSSLIllegalParam          = -9830,    /* illegal parameter */
    errSSLPeerUnknownCA         = -9831,    /* unknown Cert Authority */
    errSSLPeerAccessDenied      = -9832,    /* access denied */

    /* more errors detected by us */
    errSSLHostNameMismatch      = -9843,    /* peer host name mismatch */
    errSSLConnectionRefused     = -9844,    /* peer dropped connection before responding */
    errSSLDecryptionFail        = -9845,    /* decryption failure */
    errSSLBadRecordMac          = -9846,    /* bad MAC */
    errSSLRecordOverflow        = -9847,    /* record overflow */
    errSSLBadConfiguration      = -9848,    /* configuration error */

    //...

</figure>

但靠錯誤碼只能判斷大概的情況,很多時候并不能明確知道到底是什么原因?qū)е碌模宰钪庇^的第煮,還是需要抓包分析。

4.2 抓包分析

在這一階段磺送,使用Charles來抓包是沒有用的代虾,因為Charles是作為HTTP代理工作的,它會抓取代理的網(wǎng)絡(luò)報文艇肴,然后將報文組合成HTTP/HTTPS協(xié)議包篡撵,對于HTTP調(diào)試非常方便,但由于細節(jié)的缺失豆挽,沒辦法使用它來分析SSL相關(guān)錯誤育谬。所以我們需要使用上古神器Wireshark。

關(guān)于Wireshark就不再多介紹了帮哈,網(wǎng)上已經(jīng)有很多相關(guān)介紹和抓包教程膛檀,如《Mac OS X上使用Wireshark抓包》等,基本上可以很快上手。下面我們就以適配iOS9的ATS為例咖刃,來說下如何進行抓包分析泳炉,找出因為不支持ATS導(dǎo)致SSL握手失敗問題。

還記得SSL握手過程么嚎杨?不記得可以重溫下這篇文章:圖解SSL/TLS協(xié)議花鹅。我們也來看看Wireshark上抓取到的包來直觀學(xué)習(xí)正常的SSL握手流程:

wireshark-ssl-handshake.png

上圖是一個標準的HTTPS請求抓取的包:

  1. 在TCP三次握手成功之后,客戶端發(fā)起SSL的Client Hello(No.68幀)枫浙,傳遞隨機數(shù)(Random)刨肃,和客戶端支持的加密套件(Cipher Suites)、壓縮方法箩帚、簽名算法等信息真友; 如下圖所示,這是Client Hello所攜帶的信息紧帕,可以展開來看相關(guān)的詳情:
wireshark-ssl-client-hello.png
  1. 服務(wù)器從Client Hello中匹配支持的加密套件(Cipher Suites)盔然、壓縮算法和簽名算法,和服務(wù)器新生成的一個隨機數(shù)返回給客戶端是嗜,這就是Server Hello(No.70幀)愈案。 下圖就是對1)中Client Hello的回應(yīng),由圖可以看出鹅搪,服務(wù)端匹配的Cipher Suite是TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
wireshark-ssl-server-hello.png
  1. 服務(wù)器同時會將證書發(fā)給客戶端(No.73幀)刻帚;有時候抓取的包只有Client HelloServer Hello,而沒有再發(fā)送證書的涩嚣,這是SSL/TLS的Session重用了:由于新建立一個SSL/TLS Session的成本太高崇众,所以之前有建立SSL/TLS連接Session的話,客戶端會保存Session ID航厚,在下一次請求時在Client Hello中帶上顷歌,服務(wù)端驗證有效之后,就會成功重用Sesssion幔睬。

注:關(guān)于重用TLS Session眯漩,在特定場景下會引發(fā)嚴重的問題:當App只針對了代碼中發(fā)起的HTTPS請求做了本地證書校驗,而WebView中發(fā)起的HTTPS請求并沒有做本地證書校驗麻顶,可能會出現(xiàn)App內(nèi)代碼發(fā)起的請求直接重用WebView中建立的HTTPS鏈接赦抖,導(dǎo)致中間人可以實現(xiàn)短暫的繞過攻擊。

拓展閱讀:

  1. 客戶端確認證書有效队萤,則會生產(chǎn)最后一個隨機數(shù)(Premaster secret),并使用證書的公鑰RSA加密這個隨機數(shù)矫钓,發(fā)回給服務(wù)端要尔。為了更高的安全性舍杜,會改為Diffie-Hellman算法(簡稱DH算法);采用DH算法赵辕,最后一個隨機數(shù)(Premaster secret)是不需要傳遞的既绩,客戶端和服務(wù)端交換參數(shù)之后就可以算出。Client Key Exchange(No. 75幀)还惠;

  2. 接下來雙方都會發(fā)送Change Cipher Spec通知對方饲握,接下來的所有消息都會使用簽名約定好的密鑰進行加密通信。

  3. 最后是雙方的Finished Message(即Encrypted Handshake Message蚕键, No. 77救欧、79幀),這個消息是最終的校驗嚎幸,里面包含了握手過程中的Session Key等信息,如果對方能夠解密這個消息則表示握手成功寄猩,結(jié)束整個SSL Handshake流程嫉晶。

掌握了SSL/TLS握手流程之后,調(diào)試SSL/TLS就會變得非常簡單田篇,只需要看在哪個環(huán)節(jié)報錯(Alert)替废,就可以基本推斷出相關(guān)的錯誤。

相關(guān)SSL/TLS接口信息泊柬,請查看:RFC5246以及SSL/TLS in Detail

下面就列舉下調(diào)試適配ATS過程中遇到的主要問題:

  1. 加密套件(Cipher Suite)等參數(shù)無法匹配:加密套件不匹配是最常見的握手失敗的例子椎镣。

在ATS中,可接受的加密套件有包括:

TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA

但往往很多服務(wù)器的HTTPS配置很久沒有升級兽赁,沒辦法支持這些Cipher Suite状答;客戶端發(fā)送Client Hello給服務(wù)端,帶上支持加密套件參數(shù)刀崖;服務(wù)端查看這些參數(shù)惊科,發(fā)現(xiàn)一個都不支持,則直接返回Handshake Failure的信息亮钦。如下圖:

wireshark-handshake-failure1.png

一般在接受到客戶端發(fā)送的Client Hello后返回Handshake Failure馆截,都是因為服務(wù)端無法匹配客戶端SSL握手參數(shù)。至于是不是加密套件這個參數(shù)匹配的問題蜂莉,建議抓取取消ATS了的正常HTTPS請求包進行對比蜡娶,找出具體不匹配的參數(shù)。

  1. SSL/TLS版本過低映穗,這個也非常常見窖张,但一般會被上一個參數(shù)不匹配的錯誤所掩蓋。因為大多數(shù)SSL/TLS版本低的服務(wù)器HTTPS配置支持的加密套件等參數(shù)版本也比較低蚁滋,而SSL/TLS版本是客戶端收到Server Hello之后才驗證的荤堪,但前面握手失敗就走不到這一步了合陵。所以加密套件(Cipher Suite)等參數(shù)無法匹配支持,一般也就意味著服務(wù)端SSL/TLS版本過低澄阳。

  2. 證書鏈配置錯誤:在開發(fā)過程中拥知,本人遇到過證書鏈沒有按照順序進行配置的問題,也遇到過只配置了葉子證書的問題碎赢。對于這些問題低剔,可以直接查看SSL握手過程中,服務(wù)端返回的Certificate包:

wireshark-ssl-certificate-chain-err.png

上圖可以看到證書鏈Certificates只有一個肮塞,這是典型的配置錯誤襟齿。

PS:使用Wireshark進行抓包的時候,有時候由于一些HTTPS請求的SSL/TLS版本號太低枕赵,Wireshark沒辦法辨認其是SSL包猜欺,而是顯示為TCP;此時可以手動來Decode:選擇對應(yīng)的TCP數(shù)據(jù)幀拷窜,右鍵 -》Decode As -》Transport 選擇SSL -》Apply既可开皿。

wireshark-decode-as.png

5. 后記

這個時代,安全重要么篮昧?這是我曾常疑惑的赋荆。90%以上的大眾對安全沒有切實的概念,即使安全上了春晚懊昨,過了熱潮一切又重歸原樣窄潭。特別最近換工作到保險金融類公司,安全問題更是觸目驚心酵颁。一直相信嫉你,人如同一個圓,你知道的越多躏惋,學(xué)的越深均抽,接觸的越廣,圓就越大其掂,越知道自己的渺小油挥,越懂得敬畏。

這世界永遠不會缺少矛和盾款熬,沒有“Mission Impossible”深寥,不是么?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贤牛,一起剝皮案震驚了整個濱河市惋鹅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌殉簸,老刑警劉巖闰集,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沽讹,死亡現(xiàn)場離奇詭異,居然都是意外死亡武鲁,警方通過查閱死者的電腦和手機爽雄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沐鼠,“玉大人挚瘟,你說我怎么就攤上這事∷撬螅” “怎么了乘盖?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長憔涉。 經(jīng)常有香客問我订框,道長,這世上最難降的妖魔是什么兜叨? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任穿扳,我火速辦了婚禮,結(jié)果婚禮上浪腐,老公的妹妹穿的比我還像新娘纵揍。我一直安慰自己顿乒,他們只是感情好议街,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著璧榄,像睡著了一般特漩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上骨杂,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天涂身,我揣著相機與錄音,去河邊找鬼搓蚪。 笑死蛤售,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的妒潭。 我是一名探鬼主播悴能,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼雳灾!你這毒婦竟也來了漠酿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤谎亩,失蹤者是張志新(化名)和其女友劉穎炒嘲,沒想到半個月后宇姚,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡夫凸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年浑劳,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寸痢。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡呀洲,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出啼止,到底是詐尸還是另有隱情道逗,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布献烦,位于F島的核電站滓窍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏巩那。R本人自食惡果不足惜吏夯,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望即横。 院中可真熱鬧噪生,春花似錦、人聲如沸东囚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽页藻。三九已至桨嫁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間份帐,已是汗流浹背璃吧。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留废境,地道東北人畜挨。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像噩凹,于是被迫代替她去往敵國和親巴元。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容