HTTPS 的作用是在會(huì)話層、表示層引入 TLS/SSL 握手協(xié)議,通過數(shù)據(jù)加密、解密方式,來應(yīng)對(duì)數(shù)據(jù)明文傳輸過程中遇到的問題,保障數(shù)據(jù)的完整性、一致性,為用戶帶來更安全的網(wǎng)絡(luò)體驗(yàn)、更好的隱私保護(hù)。
然而,HTTPS 增加了 TLS/SSL 握手環(huán)節(jié),再加上應(yīng)用數(shù)據(jù)傳輸需要經(jīng)過對(duì)稱加密,對(duì)性能提出了更大的挑戰(zhàn)。
作為一個(gè)好的架構(gòu),一定要均衡安全和性能兩方面,如果讓天秤向任何一方傾斜過多,都會(huì)影響最終的用戶體驗(yàn)。
因此,為了兼顧安全與性能,蘇寧的全站 HTTPS 改造從 2015 年底開始進(jìn)行,歷時(shí)一年多時(shí)間,主要做了系統(tǒng) HTTPS 改造、HTTPS 性能優(yōu)化和 HTTPS 灰度上線這三方面工作,讓用戶在 HTTPS 下訪問能夠獲得極致體驗(yàn)成為了可能。
全站 HTTPS 方案概述
蘇寧易購(gòu)從 2015 年開始規(guī)劃做 HTTPS 相關(guān)的事情,當(dāng)時(shí)可借鑒的資料非常少,電商類網(wǎng)站相關(guān)的 HTTPS 改造的詳盡案例更是難求。
如下圖,是蘇寧易購(gòu)全站的 HTTPS 方案:
如圖中所示,整個(gè)方案分三步構(gòu)建,分別是系統(tǒng)改造、性能優(yōu)化和灰度上線:
系統(tǒng)改造。原有系統(tǒng)想要支持 HTTPS,必須進(jìn)行改造,首先要建立 HTTPS 接入層,也就是開通 443 端口,讓所有的應(yīng)用系統(tǒng)支持 HTTPS 訪問。
在此基礎(chǔ)上做頁(yè)面資源替換,解決當(dāng)一個(gè) HTTPS 頁(yè)面出現(xiàn) HTTP 請(qǐng)求時(shí)就會(huì)出現(xiàn)錯(cuò)誤的問題。做完這兩件事,CDN 上證書的處理、HTTPS 測(cè)試方案等問題也就迎刃而解。
性能優(yōu)化。做系統(tǒng)改造,增加兩次 TLS 握手,必然會(huì)對(duì)性能造成一定的開銷和損失,如何去彌補(bǔ)性能的損失,達(dá)到性能和安全兼顧呢?性能優(yōu)化部分包含若干優(yōu)化點(diǎn),下文會(huì)詳細(xì)展開。
灰度上線。唐山網(wǎng)站建設(shè)這部分是時(shí)間花費(fèi)最多的,HTTPS 一步步上線的過程中,踩坑最多,其中部分是前面沒有發(fā)現(xiàn)的問題。
這證明不能一次性將整個(gè)全站、全地區(qū)、全用戶一次性堆成 HTTPS,可以根據(jù)流量所處的運(yùn)營(yíng)商和城市及用戶級(jí)別去做灰度上線。
HTTPS 方案之系統(tǒng)改造篇
01、HTTPS 接入層定義
系統(tǒng)改造的頭等大事是開通 443 端口,成熟的網(wǎng)絡(luò)系統(tǒng)會(huì)包含 CDN、硬件負(fù)載均衡、應(yīng)用防火墻、Web 服務(wù)器、應(yīng)用服務(wù)器,最后到數(shù)據(jù)層。難道整個(gè)鏈路都要做 HTTPS?在每層都增加 SSL 握手消耗嗎?答案是否定的。
所以,應(yīng)該盡早完成 SSL 握手,做 SSL 過程中首要考慮的是 HTTPS 接入層的定位。
如下圖,是蘇寧易購(gòu)架構(gòu)中 HTTPS 接入層的位置:
如圖中所示,我們把 HTTPS 接入層放在 CDN 和應(yīng)用系統(tǒng)之間,采用四層+七層負(fù)載均衡的架構(gòu)。
四層負(fù)載并不處理 HTTPS 卸載,它的主要職責(zé)是做 TCP 的分發(fā)。在七層負(fù)載完成整個(gè) SSL 握手,而后面應(yīng)用系統(tǒng)走 80 端口,這樣就相當(dāng)于完成了 HTTPS 整個(gè)卸載的過程。
這樣做的好處,一方面,系統(tǒng)應(yīng)用層面不需要為 HTTPS 做任何調(diào)整;另一方面,將來所有 HTTPS 的調(diào)度、優(yōu)化和配置都可以在接入層完成。
02、頁(yè)面資源替換
第一步,理解 Mixed Content
對(duì)于一個(gè)頁(yè)面而言,請(qǐng)求頁(yè)面的請(qǐng)求是用 HTTPS 加載,一旦內(nèi)部頁(yè)面元素有 HTTP 的性質(zhì),這時(shí) RFC 標(biāo)準(zhǔn)里就會(huì)出現(xiàn)一個(gè)錯(cuò)誤,叫 Mixed Content(混淆錯(cuò)誤)。
所以,如果要加載一個(gè)安全的 HTTPS 頁(yè)面,就不應(yīng)該在其中混淆 HTTP 請(qǐng)求。
第二步,// 替換 http://
用 // 替換 http://,這樣就可以讓頁(yè)面所有的元素做一個(gè)適配,去遵循原來的請(qǐng)求。
第三步,x-request-url 的定義和使用
當(dāng)然,我們?cè)?/替換過程中也遇到了一些坑。舉個(gè)例子,下圖是蘇寧易購(gòu)單點(diǎn)登錄系統(tǒng)交互的過程:
如圖中所示,當(dāng)用戶 authID 失效,發(fā)起請(qǐng)求 https://xxx.suning.com/authStatus 鑒權(quán),接入層會(huì)對(duì)所有請(qǐng)求做卸載,地址就會(huì)變成 HTTP。
進(jìn)入業(yè)務(wù)系統(tǒng)做鑒權(quán)的話,Reponse 302 就會(huì)跳轉(zhuǎn)到單點(diǎn)登錄系統(tǒng)。這時(shí)會(huì)將第二步的頁(yè)面記錄為原始頁(yè)面,返回到用戶端,用戶去請(qǐng)求單點(diǎn)登錄系統(tǒng),單點(diǎn)登錄系統(tǒng)完成鑒權(quán)以后,再回跳時(shí),是 HTTP 地址,最終導(dǎo)致用戶端 MixContent。
因此,我們引入 x-request-url 解決問題,如下圖:
所有原始請(qǐng)求協(xié)議都記錄在 x-request-url 中,如果業(yè)務(wù)系統(tǒng)鑒權(quán),一定要遵循 x-request-url 記錄的協(xié)議,就可應(yīng)對(duì)回跳導(dǎo)致的用戶端 Mix Content 問題。