議深度解析:從握手原理到HTTPS部署排錯(cuò))
在日常開(kāi)發(fā)里SSL 這個(gè)詞出現(xiàn)的頻率比我們想象的還要高可坦白講真正把它搞明白的人并不太多。我見(jiàn)過(guò)不少同學(xué)上線(xiàn) HTTPS 的時(shí)候證書(shū)一貼、端口一配就以為完事了結(jié)果線(xiàn)上直接冒出一堆協(xié)議告警有的用戶(hù)反復(fù)掉線(xiàn)有的接口被中間設(shè)備攔截排查一整天最后才發(fā)現(xiàn)服務(wù)器只開(kāi)了老舊的 TLS 1.0或者證書(shū)鏈根本沒(méi)配全。這篇就圍繞SSL 協(xié)議從底層的設(shè)計(jì)思路開(kāi)始拆一直講到握手過(guò)程的每一個(gè)字節(jié)級(jí)細(xì)節(jié)、證書(shū)體系的校驗(yàn)規(guī)則、部署時(shí)的參數(shù)取舍和排錯(cuò)手段把這套經(jīng)常被“糊弄過(guò)去”的協(xié)議說(shuō)透適合剛接觸 HTTPS 的后端開(kāi)發(fā)者、前段工程師以及所有想搞懂網(wǎng)絡(luò)層安全的人。1. SSL 協(xié)議到底在解決什么問(wèn)題1.1 我最早接觸 SSL是在一個(gè)支付回調(diào)的隱患現(xiàn)場(chǎng)先講個(gè)真實(shí)的場(chǎng)景。某天我在排查一個(gè)電商項(xiàng)目的接口日志發(fā)現(xiàn)一個(gè)詭異的現(xiàn)象服務(wù)端返回給用戶(hù)的關(guān)鍵數(shù)據(jù)在傳輸途中某個(gè)節(jié)點(diǎn)上出現(xiàn)了被篡改的痕跡。用戶(hù)下單金額是 199到服務(wù)端之后變成了 1999訂單直接報(bào)廢對(duì)賬也亂了。那會(huì)兒業(yè)務(wù)方還在用純 HTTP 跑核心接口出了問(wèn)題之后所有人第一反應(yīng)都是查后端邏輯查數(shù)據(jù)庫(kù)查緩存折騰了一整天才有人想起來(lái)你們的網(wǎng)絡(luò)傳輸是明文的。這個(gè)案例特別適合用來(lái)理解 SSL 存在的意義。可以把網(wǎng)絡(luò)傳輸想成郵局里寄平信信封上沒(méi)封口任何一個(gè)經(jīng)手的郵遞員都能打開(kāi)看、能改內(nèi)容、還能換一封信塞進(jìn)去。HTTP 就是這種沒(méi)封口的平信數(shù)據(jù)從瀏覽器到服務(wù)器中間經(jīng)過(guò)的每一跳路由器、交換機(jī)、代理服務(wù)器只要有權(quán)限都可以直接讀取和分析所以在沒(méi)有加密的網(wǎng)絡(luò)上談業(yè)務(wù)安全等于把銀行卡密碼寫(xiě)在便利貼上貼在胸口上街。SSL 協(xié)議全稱(chēng)是安全套接層后來(lái)標(biāo)準(zhǔn)化成 TLS也就是傳輸層安全核心目標(biāo)就是解決這三個(gè)問(wèn)題第一別人能不能偷看這次通信的內(nèi)容這叫機(jī)密性第二內(nèi)容在傳輸途中被改動(dòng)之后接收方能不能第一時(shí)間發(fā)現(xiàn)這叫完整性第三客戶(hù)端要訪(fǎng)問(wèn)的這個(gè)服務(wù)器到底是不是它聲稱(chēng)的那個(gè)服務(wù)器這叫身份驗(yàn)證。報(bào)文里用引號(hào)強(qiáng)調(diào)過(guò)這三件事因?yàn)樗鼈冋娴臅?huì)對(duì)應(yīng)到完全不同的密碼學(xué)機(jī)制上而在實(shí)際工程里缺了任何一個(gè)環(huán)節(jié)都會(huì)出大事故。1.2 三個(gè)核心能力一個(gè)都不能少先說(shuō)機(jī)密性這是大家最熟悉的加密傳輸?,F(xiàn)代密碼學(xué)里加密分對(duì)稱(chēng)加密和非對(duì)稱(chēng)加密對(duì)稱(chēng)加密好比兩個(gè)人共用一把鑰匙加密解密都用同一把速度快、開(kāi)銷(xiāo)小但問(wèn)題是這把鑰匙怎么安全地傳給對(duì)方非對(duì)稱(chēng)加密則是一對(duì)鑰匙公鑰誰(shuí)都能拿私鑰只自己留著用公鑰加密的數(shù)據(jù)只有私鑰能解反之亦然解決了鑰匙分發(fā)的問(wèn)題但性能比對(duì)稱(chēng)加密慢幾個(gè)數(shù)量級(jí)。SSL 協(xié)議的聰明之處在于它不二選一而是把兩者組合起來(lái)握手階段先用非對(duì)稱(chēng)加密去協(xié)商出一個(gè)會(huì)話(huà)專(zhuān)用的對(duì)稱(chēng)密鑰之后整個(gè)連接的數(shù)據(jù)通信用對(duì)稱(chēng)加密來(lái)跑。這個(gè)思路在工程里被叫“混合加密”因?yàn)榧饶玫搅朔菍?duì)稱(chēng)加密的安全性和便利性又不犧牲大數(shù)據(jù)量傳輸時(shí)對(duì)稱(chēng)加密的速度處理一張圖片、一個(gè) JSON 包開(kāi)銷(xiāo)基本可以忽略。再說(shuō)完整性這件事很多人容易忽略。加密只能保證內(nèi)容不會(huì)被偷看不能保證內(nèi)容不會(huì)被篡改。為了檢測(cè)篡改SSL 引入了帶密鑰的消息摘要發(fā)送方用會(huì)話(huà)密鑰對(duì)報(bào)文內(nèi)容算一個(gè)固定長(zhǎng)度的校驗(yàn)值接收方收到后用同一個(gè)密鑰重新算一遍兩邊對(duì)不上就說(shuō)明數(shù)據(jù)在途中被動(dòng)過(guò)手腳直接斷開(kāi)連接。有同學(xué)會(huì)問(wèn)既然密鑰只有雙方知道那這個(gè)機(jī)制為什么不能替代加密因?yàn)橥暾院蜋C(jī)密性的側(cè)重點(diǎn)完全不同有些安全需求里可以只驗(yàn)真不改密但在 SSL 的模型里兩者必須同時(shí)啟用這也是協(xié)議設(shè)計(jì)得很?chē)?yán)謹(jǐn)?shù)牡胤?。最后是身份?yàn)證這在實(shí)際使用中最容易被誤解。很多初學(xué)者以為只要瀏覽器地址欄的小鎖頭亮了數(shù)據(jù)就安全了其實(shí)背后是復(fù)雜的證書(shū)鏈驗(yàn)證機(jī)制。服務(wù)器要向客戶(hù)端證明“我是我”需要出示一份由公認(rèn)的證書(shū)頒發(fā)機(jī)構(gòu)簽名的數(shù)字證書(shū)客戶(hù)端拿到證書(shū)之后會(huì)沿著證書(shū)鏈一級(jí)一級(jí)往上驗(yàn)證直到找到自己內(nèi)置信任的根證書(shū)然后驗(yàn)證證書(shū)里的簽名有沒(méi)有被篡改、有效期有沒(méi)有過(guò)期、域名是否匹配全部通過(guò)之后才真正建立起信任。這個(gè)體系對(duì)應(yīng)到現(xiàn)實(shí)生活有點(diǎn)像護(hù)照系統(tǒng)護(hù)照由權(quán)威機(jī)構(gòu)頒發(fā)邊檢人員不認(rèn)護(hù)照本身而是認(rèn)護(hù)照簽發(fā)機(jī)構(gòu)、驗(yàn)證防偽特征、核對(duì)信息里的照片和姓名確認(rèn)無(wú)誤之后才讓你入境。2. TLS 版本演進(jìn)與加密套件選型2.1 為什么 2025 年還在說(shuō) SSL但實(shí)際用的全是 TLS嚴(yán)格來(lái)說(shuō)SSL 協(xié)議已經(jīng)“死”了很多年了。1994 年網(wǎng)景推出了 SSL 2.01995 年升級(jí)到 SSL 3.0隨后 IETF 接手并在 SSL 3.0 基礎(chǔ)上發(fā)布了 TLS 1.0之后所有新版本都叫 TLS。所以現(xiàn)在市面上所有號(hào)稱(chēng)支持 SSL 的軟件、設(shè)備和系統(tǒng)真正實(shí)現(xiàn)并且啟用的協(xié)議基本都是 TLS 1.2 或 TLS 1.3“SSL”這個(gè)稱(chēng)呼只不過(guò)是因?yàn)闅v史慣性被沿用成了一個(gè)行業(yè)黑話(huà)。這想起來(lái)有點(diǎn)像現(xiàn)在很多人還在說(shuō)“傳真機(jī)”盡管幾乎沒(méi)人再用傳真了但這個(gè)詞就是留在口語(yǔ)里揮之不去。不過(guò)對(duì)工程來(lái)說(shuō)叫法不能太含糊因?yàn)榘姹局g的差距不是修修補(bǔ)補(bǔ)而是代差級(jí)的。版本演變的大致脈絡(luò)可以這樣看SSL 2.0 因?yàn)樵O(shè)計(jì)缺陷很快被淘汰SSL 3.0 在 2014 年被 POODLE 攻擊徹底擊穿以后也被棄用TLS 1.0 和 TLS 1.1 同樣在 2020 年之后被主流瀏覽器相繼停止支持因?yàn)樗鼈兌蓟谂f的加密套件體系很多不再安全的算法一個(gè)都沒(méi)有退出舞臺(tái)。真正意義上還能在公開(kāi)互聯(lián)網(wǎng)上安全使用的只有 TLS 1.2 和 TLS 1.3 兩個(gè)版本而 1.3 在握手流程、前向保密、性能優(yōu)化上又把 1.2 遠(yuǎn)遠(yuǎn)甩在后面。理解了歷史你就明白為什么排查老舊系統(tǒng)的時(shí)候總是會(huì)遇到“協(xié)議版本過(guò)低”的報(bào)錯(cuò)。這個(gè)問(wèn)題不是配置寫(xiě)錯(cuò)了而是老版本的協(xié)議從根上就不可信就好比房子地基用了不合格的混凝土再怎么裝修表面承重結(jié)構(gòu)都已經(jīng)不牢靠了。2.2 加密套件到底怎么讀第一次接觸加密套件的人大概率會(huì)被那一長(zhǎng)串名字嚇到比如TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256??粗駚y碼其實(shí)讀法很規(guī)則拆開(kāi)就是面粉、水和鹽的關(guān)系。前綴TLS_表明協(xié)議族ECDHE表示密鑰交換算法RSA表示證書(shū)簽名算法AES_128_GCM表示對(duì)稱(chēng)加密算法和分組模式SHA256表示消息校驗(yàn)所用的哈希算法??蛻?hù)端的加密套件列表是一份“我會(huì)的菜”的菜單服務(wù)器在這份菜單里選出自己也會(huì)做、且策略允許的一道菜雙方用同一套組合完成整個(gè)會(huì)話(huà)。這里有兩個(gè)值得注意的點(diǎn)。第一ECDHE屬于臨時(shí)的橢圓曲線(xiàn) Diffie-Hellman 密鑰交換它每次握手都會(huì)生成隨機(jī)的臨時(shí)密鑰對(duì)然后用臨時(shí)私鑰參與會(huì)話(huà)密鑰的計(jì)算這個(gè)過(guò)程配合“計(jì)算完之后臨時(shí)密鑰不落盤(pán)、用完即丟”的行為能夠?qū)崿F(xiàn)前向保密。也就是說(shuō)即使服務(wù)器長(zhǎng)期的私鑰被盜攻擊者也拿不到歷史流量的會(huì)話(huà)密鑰沒(méi)辦法解密以前的通信記錄這個(gè)特性在 2013 年斯諾登事件之后被各大廠(chǎng)商瘋狂推行成了現(xiàn)代安全的標(biāo)配。第二AES_128_GCM里的 GCM 是 AEAD 認(rèn)證加密模式它把加密和完整性校驗(yàn)壓縮到一個(gè)原子操作里性能和安全性都比舊的 CBC 模式更優(yōu)而且算法在設(shè)計(jì)上是流式驅(qū)動(dòng)對(duì)現(xiàn)代 CPU 的 AES-NI 硬件指令集非常友好加密數(shù)據(jù)量大的時(shí)候速度提升極其明顯。選加密套件其實(shí)就是在權(quán)衡這三個(gè)維度安全性、性能和兼容性?xún)?yōu)秀的配置通常是越短越好前面那串看起來(lái)很長(zhǎng)實(shí)際在協(xié)商記錄里占用的空間也就幾百個(gè)字節(jié)。2.3 實(shí)際選型建議與一份可抄的參考配置談到服務(wù)端具體配置我用 Nginx 舉例能覆蓋絕大多數(shù)場(chǎng)景。一個(gè)現(xiàn)代化網(wǎng)站或者 API 服務(wù)的 TLS 配置可以按這個(gè)思路寫(xiě)server { listen 443 ssl http2; server_name example.com; ssl_certificate /etc/nginx/certs/example.com_fullchain.crt; ssl_certificate_key /etc/nginx/certs/example.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; }這套配置的核心思想有三條。協(xié)議只用 TLS 1.2 和 1.3作為底線(xiàn)拒絕一切老版本可以擋掉絕大多數(shù)針對(duì)舊版本的降級(jí)攻擊加密套件只保留 ECDHE 開(kāi)頭的套件也就是強(qiáng)制前向保密并且優(yōu)先支持 GCM 模式性能和安全兼顧會(huì)話(huà)緩存打開(kāi)、會(huì)話(huà)票證關(guān)掉這樣既能讓重復(fù)訪(fǎng)問(wèn)的用戶(hù)免除重復(fù)握手又不會(huì)因?yàn)槠弊C私鑰泄露而影響全站安全性。實(shí)際部署的時(shí)候還建議順手把 OCSP Stapling 打開(kāi)這個(gè)機(jī)制后面單獨(dú)講一句話(huà)概括就是把證書(shū)狀態(tài)的在線(xiàn)查詢(xún)結(jié)果直接“捎帶”給客戶(hù)端減少客戶(hù)端額外查一次簽發(fā)機(jī)構(gòu)的延遲并對(duì)抗某些不安全的在線(xiàn)狀態(tài)查詢(xún)。3. 握手協(xié)議完整拆解3.1 TLS 1.2 的完整握手過(guò)程每一步都發(fā)生了什么握手是整個(gè) SSL 協(xié)議里最精彩的部分也是排查各種疑難問(wèn)題的核心入口??梢园阉胂蟪蓛蓚€(gè)陌生人第一次見(jiàn)面時(shí)的互驗(yàn)身份和交換保險(xiǎn)柜密碼的過(guò)程TLS 1.2 的完整握手有四個(gè)基本回合。第一回合客戶(hù)端發(fā)送ClientHello。里面包含當(dāng)前支持的 TLS 版本號(hào)、一個(gè)隨機(jī)數(shù)client_random、一個(gè)會(huì)話(huà) ID、加密套件列表和一些擴(kuò)展字段。這個(gè)隨機(jī)數(shù)非常重要后面會(huì)參與會(huì)話(huà)密鑰的計(jì)算它必須每次都不一樣防止重放攻擊。第二回合服務(wù)器返回ServerHello。服務(wù)器從客戶(hù)端列出的加密套件里選出一組并且?guī)献约旱碾S機(jī)數(shù)server_random再加上服務(wù)器證書(shū)。緊接著服務(wù)器會(huì)發(fā)送ServerKeyExchange這個(gè)報(bào)文攜帶的是 ECDHE 需要的服務(wù)器臨時(shí)公鑰以及服務(wù)器用自己私鑰對(duì)關(guān)鍵參數(shù)做的簽名最后服務(wù)器發(fā)ServerHelloDone意思是“我能提供的就是這些輪到你表態(tài)了”。第三回合客戶(hù)端動(dòng)作最多。它先驗(yàn)證服務(wù)器證書(shū)的合法性驗(yàn)證通過(guò)后取出證書(shū)里的服務(wù)器公鑰構(gòu)造自己的 ECDHE 臨時(shí)密鑰對(duì)并向服務(wù)器發(fā)送ClientKeyExchange內(nèi)容是自己那部分臨時(shí)公鑰。到這一步客戶(hù)端本地已經(jīng)同時(shí)擁有了client_random、server_random和雙方的臨時(shí)公鑰可以計(jì)算得出預(yù)主密鑰再經(jīng)過(guò)PRF偽隨機(jī)函數(shù)派生出真正的會(huì)話(huà)密鑰??蛻?hù)端隨后發(fā)送ChangeCipherSpec告訴服務(wù)器“接下來(lái)我要開(kāi)始加密了”最后發(fā)一條用會(huì)話(huà)密鑰加密的Finished消息。第四回合服務(wù)器在收到ClientKeyExchange后同樣計(jì)算會(huì)話(huà)密鑰校驗(yàn)客戶(hù)端的Finished消息如果一致就說(shuō)明協(xié)商成功。服務(wù)器也回一條自己的ChangeCipherSpec和加密Finished客戶(hù)端校驗(yàn)通過(guò)握手結(jié)束。有幾個(gè)細(xì)節(jié)值得多強(qiáng)調(diào)。握手階段涉及三個(gè)隨機(jī)數(shù)很多人不理解為什么不能直接用兩個(gè)因?yàn)槠渲幸粋€(gè)隨機(jī)數(shù)如果被攻擊者提前預(yù)測(cè)整個(gè)會(huì)話(huà)密鑰就存在被計(jì)算出來(lái)的風(fēng)險(xiǎn)引入第三個(gè)隨機(jī)數(shù)能把不可預(yù)測(cè)性拉得很高。另外在握手過(guò)程中雙方的臨時(shí)密鑰只存在于內(nèi)存配合非對(duì)稱(chēng)簽名完成身份認(rèn)證之后私鑰的作用就結(jié)束了這是前向保密的核心思路。還有一條Finished消息的內(nèi)容是到目前為止所有握手消息的哈希值任何一個(gè)字節(jié)被中間人改動(dòng)握手都會(huì)在這一步失敗客戶(hù)端或服務(wù)端會(huì)直接終止連接。這段握手流程在真實(shí)抓包時(shí)會(huì)非常清晰如果你用 Wireshark 過(guò)濾tls.handshake協(xié)議就能看到Client Hello、Server Hello、Certificate、Server Key Exchange、Client Key Exchange、New Session Ticket等連續(xù)的報(bào)文每個(gè)報(bào)文里的字段都能和上面描述的步驟一一對(duì)應(yīng)。3.2 TLS 1.3 把握手壓縮到一個(gè)來(lái)回TLS 1.3 最大的調(diào)整是重新設(shè)計(jì)握手流程把原來(lái)多個(gè)來(lái)回的消息壓縮成了兩個(gè)報(bào)文??蛻?hù)端第一回合同時(shí)發(fā)出自己的密鑰協(xié)商參數(shù)和支持的加密套件服務(wù)器收到后直接回復(fù)自己的密鑰協(xié)商參數(shù)、證書(shū)和Finished這個(gè)來(lái)回結(jié)束之后客戶(hù)端和服務(wù)器就可以立刻開(kāi)始發(fā)送加密應(yīng)用數(shù)據(jù)整個(gè)過(guò)程只有一個(gè)網(wǎng)絡(luò)來(lái)回。做到這一點(diǎn)靠的是兩個(gè)狠招。第一去掉了 RSA 密鑰交換、CBC 模式、SHA-1 等所有舊算法強(qiáng)制使用 ECDHE 和 AEAD加密套件數(shù)量從幾十個(gè)砍到只剩幾個(gè)避免了協(xié)商過(guò)程中大量參數(shù)帶來(lái)的復(fù)雜性和潛在攻擊面。第二在ClientHello里直接帶上客戶(hù)端的密鑰共享參數(shù)比如基于 X25519 的臨時(shí)公鑰讓服務(wù)器拿到就能立刻計(jì)算密鑰而不必等第二個(gè)來(lái)回再交換材料。這種簡(jiǎn)化的好處是肉眼可見(jiàn)的。握手所需的消息暴露面更小中間人能做的壞動(dòng)作更少每次新連接的延遲從 2-RTT 降到了 1-RTT對(duì)移動(dòng)端弱網(wǎng)場(chǎng)景的提升極為明顯。如果客戶(hù)端之前和服務(wù)器成功連接過(guò)還拿到了會(huì)話(huà)恢復(fù)票據(jù)那么在ClientHello里直接攜帶pre_shared_key可以實(shí)現(xiàn) 0-RTT 握手也就是客戶(hù)端第一包就可以帶上應(yīng)用數(shù)據(jù)一起發(fā)出去。不過(guò) 0-RTT 在語(yǔ)義上有一個(gè)天然缺陷發(fā)送的那部分是“不抗重放”的。攻擊者如果截獲了 0-RTT 數(shù)據(jù)包原樣轉(zhuǎn)發(fā)到服務(wù)器服務(wù)器無(wú)法區(qū)分這是不是合法的第一次請(qǐng)求。所以 0-RTT 只適用于冪等請(qǐng)求比如查詢(xún)接口和心跳檢測(cè)涉及修改狀態(tài)的業(yè)務(wù)堅(jiān)決不能用這是很多團(tuán)隊(duì)在引入 HTTP/3 的時(shí)候踩過(guò)的最大的一個(gè)坑。3.3 握手延遲優(yōu)化的幾種實(shí)操手段把握手延遲控制住對(duì)用戶(hù)體驗(yàn)的提升不是一點(diǎn)半點(diǎn)。首屏加載如果多兩次跨洋握手每秒速度直接垮。實(shí)際工程里有效的優(yōu)化方案有四個(gè)層次。第一個(gè)是復(fù)用會(huì)話(huà)。TLS 1.2 時(shí)代用會(huì)話(huà)緩存和會(huì)話(huà)票證TLS 1.3 時(shí)代改用New Session Ticket效果都是“這一次驗(yàn)證過(guò)的身份和商定過(guò)的密鑰下一次別再?gòu)念^來(lái)一遍”。會(huì)話(huà)緩存是服務(wù)器端存儲(chǔ)會(huì)話(huà) ID用共享內(nèi)存存會(huì)話(huà) ID 和密鑰會(huì)話(huà)票證是服務(wù)器把會(huì)話(huà)狀態(tài)加密之后作為票據(jù)下發(fā)客戶(hù)端客戶(hù)端下次原樣帶回。票證的缺點(diǎn)是要在服務(wù)器上維護(hù)票證密鑰而且這個(gè)密鑰如果泄露歷史會(huì)話(huà)也會(huì)跟著完蛋所以票證有效期要設(shè)置得短一點(diǎn)并且頻繁換新。第二個(gè)是啟用 TLS False Start。這個(gè)機(jī)制允許客戶(hù)端在第三回合確認(rèn)服務(wù)器Finished之后就提前發(fā)送應(yīng)用數(shù)據(jù)不必等自己的Finished被服務(wù)器確認(rèn)相當(dāng)于把 1-RTT 握手再壓半拍。第三個(gè)是前面提到的 OCSP Stapling。證書(shū)過(guò)期或者被吊銷(xiāo)的時(shí)候客戶(hù)端通常需要去查一下證書(shū)頒發(fā)機(jī)構(gòu)的在線(xiàn)證書(shū)狀態(tài)協(xié)議服務(wù)這一步因?yàn)橐~外發(fā)起一次 HTTP 請(qǐng)求拖慢了握手。OCSP Stapling 讓服務(wù)器自己提前把查詢(xún)結(jié)果緩存好并隨握手包直接帶給客戶(hù)端客戶(hù)端省掉了這個(gè)外部查詢(xún)。第四個(gè)是 TCP 層的優(yōu)化比如 TCP Fast Open 和調(diào)大初始擁塞窗口。TLS 雖然運(yùn)行在 TCP 之上但 TCP 自身的握手和慢啟動(dòng)同樣會(huì)吃掉往返時(shí)間。在已經(jīng)全面使用 TLS 1.3 和 HTTP/2 的環(huán)境里進(jìn)一步壓低首包延遲的終極手段就是上 HTTP/3 的 QUIC把傳輸層握手和密鑰協(xié)商完全融合在一起。這里面的代價(jià)是要額外引入 UDP 和 QUIC 協(xié)議棧屬于大項(xiàng)目級(jí)別的改造普通業(yè)務(wù)按前面三個(gè)層次優(yōu)化基本夠用。4. 證書(shū)體系與 HTTPS 部署中的常見(jiàn)坑4.1 證書(shū)鏈校驗(yàn)到底校驗(yàn)了什么前面提到了證書(shū)鏈這里展開(kāi)講因?yàn)樽C書(shū)相關(guān)的坑在 HTTPS 部署的報(bào)錯(cuò)里起碼占七成。證書(shū)鏈由三級(jí)組成最底層是服務(wù)器自己的葉子證書(shū)中間是簽發(fā)該證書(shū)的中間證書(shū)最上面是瀏覽器或操作系統(tǒng)中內(nèi)置的根證書(shū)??蛻?hù)端在驗(yàn)證服務(wù)器證書(shū)時(shí)會(huì)先檢查葉子證書(shū)的域名和服務(wù)器域名是否一致然后檢查葉子證書(shū)的簽名是不是由某個(gè)中間證書(shū)的私鑰簽的再去驗(yàn)證中間證書(shū)的簽名是不是由根證書(shū)的私鑰簽的根證書(shū)在操作系統(tǒng)里被預(yù)先信任所以驗(yàn)證鏈到這里終止。實(shí)際部署中最常見(jiàn)的錯(cuò)誤是只上傳了葉子證書(shū)沒(méi)有上傳中間證書(shū)。瀏覽器拿到的證書(shū)鏈不完整無(wú)法通過(guò)中間證書(shū)把葉子證書(shū)和根證書(shū)連接起來(lái)于是直接判定為不可信。這個(gè)問(wèn)題的排查方法很簡(jiǎn)單用瀏覽器打開(kāi)站點(diǎn)查看證書(shū)路徑如果中間證書(shū)那一格顯示“證書(shū)不受信任”幾乎就可以斷定是缺少中間證書(shū)了。另外一個(gè)坑和 SAN 字段有關(guān)。老一些的證書(shū)體系依賴(lài)證書(shū) CN 字段匹配域名但現(xiàn)代瀏覽器只看 SAN也就是主題備用名稱(chēng)字段。如果你申請(qǐng)的證書(shū) SAN 里只寫(xiě)了www.example.com那用戶(hù)直接訪(fǎng)問(wèn)example.com照樣會(huì)報(bào)證書(shū)不匹配。所以申請(qǐng)證書(shū)時(shí)要明確列全所有需要覆蓋的域名包括根域、泛域、子域和可能的跳轉(zhuǎn)目標(biāo)避免“所有頁(yè)面都開(kāi)了 HTTPS 卻每個(gè)頁(yè)面都有紅叉”的尷尬。自簽名證書(shū)也是很多人最初接觸這個(gè)概念時(shí)會(huì)做的事情。測(cè)試環(huán)境用自簽名證書(shū)完全沒(méi)問(wèn)題客戶(hù)端手動(dòng)導(dǎo)入根證書(shū)就可以建立信任但生產(chǎn)環(huán)境絕對(duì)不能這么做因?yàn)榭蛻?hù)端永遠(yuǎn)不會(huì)主動(dòng)信任一個(gè)自己不認(rèn)識(shí)的人給自己發(fā)的“身份證”。如果真的因?yàn)槟承﹥?nèi)網(wǎng)系統(tǒng)必須用自簽名證書(shū)務(wù)必配合受控的根證書(shū)分發(fā)機(jī)制并且把系統(tǒng)能做到的審計(jì)和提醒都加上。4.2 我在部署 HTTPS 時(shí)踩過(guò)的最典型的幾個(gè)坑第一個(gè)坑證書(shū)鏈不完整。某次上線(xiàn)新域名后我排查訪(fǎng)問(wèn)報(bào)錯(cuò)用openssl s_client連接目標(biāo)端口看到返回的證書(shū)只有一個(gè)葉子證書(shū)瀏覽器不管是哪個(gè)家全都提示鏈斷裂。解決辦法是找到簽發(fā)機(jī)構(gòu)提供的中間證書(shū)文件合并到服務(wù)器證書(shū)后面一起配置在 Nginx 里把證書(shū)文件改成“葉子證書(shū) 中間證書(shū)”拼接的內(nèi)容重啟后問(wèn)題消失。第二個(gè)坑TLS 版本和加密套件不匹配。有些老客戶(hù)端特別是某些 Android 4.0 時(shí)代的系統(tǒng)內(nèi)置瀏覽器幾乎不支持 ECDHE 和 TLS 1.2服務(wù)端強(qiáng)制啟用新協(xié)議之后這些用戶(hù)會(huì)出現(xiàn)大面積無(wú)法訪(fǎng)問(wèn)。取舍的辦法通常是看業(yè)務(wù)受眾如果是面向公眾平臺(tái)的站點(diǎn)可以考慮在 Nginx 里對(duì)老協(xié)議做條件兼容比如單獨(dú)開(kāi)一個(gè)端口給老客戶(hù)端如果是辦公系統(tǒng)面向內(nèi)部設(shè)備設(shè)備盡在 IT 掌控下直接全部升級(jí)到 TLS 1.3 是最干凈的選擇。第三個(gè)坑HTTP 強(qiáng)跳轉(zhuǎn)導(dǎo)致會(huì)話(huà)丟失。很多站點(diǎn)配了 HTTP 自動(dòng)跳轉(zhuǎn) HTTPS但如果跳轉(zhuǎn)只響應(yīng) 301 而沒(méi)有關(guān)心請(qǐng)求里的Cookie或者路徑會(huì)出現(xiàn)用戶(hù)從http://進(jìn)來(lái)之后登錄狀態(tài)消失的問(wèn)題。這個(gè)事兒本質(zhì)上不是 TLS 的問(wèn)題是應(yīng)用層狀態(tài)管理的問(wèn)題但在排查時(shí)容易讓人誤以為是證書(shū)或者協(xié)議問(wèn)題。第四個(gè)坑混合內(nèi)容。網(wǎng)站從 HTTP 升級(jí) HTTPS 之后頁(yè)面上引用的圖片、腳本、樣式表路徑仍然以http://開(kāi)頭瀏覽器會(huì)攔截這些資源控制臺(tái)會(huì)報(bào)混合內(nèi)容錯(cuò)誤。修復(fù)方法是凡是在頁(yè)面里的靜態(tài)資源都使用相對(duì)路徑或者協(xié)議相對(duì)路徑再不行就在部署管道里強(qiáng)制把所有靜態(tài)引用改寫(xiě)成https://。每一個(gè)坑都對(duì)應(yīng)到具體的ssl_protocols、證書(shū)鏈、域名匹配或者應(yīng)用層路徑處理排查的時(shí)候先明確報(bào)錯(cuò)層別上來(lái)就抓包。4.3 性能開(kāi)銷(xiāo)到底有多大很多團(tuán)隊(duì)對(duì)啟用 HTTPS 有顧慮擔(dān)心性能扛不住。這個(gè)擔(dān)心在十年前是合理的但在現(xiàn)在的主流硬件上只要配置合理TLS 帶來(lái)的性能損耗已經(jīng)小到可以忽略。開(kāi)銷(xiāo)主要來(lái)自?xún)刹糠治帐蛛A段的非對(duì)稱(chēng)計(jì)算和應(yīng)用數(shù)據(jù)階段的對(duì)稱(chēng)加密。對(duì)稱(chēng)加密在啟用 AES-NI 指令集的 CPU 上一個(gè)核心每秒可以加密數(shù) GB 數(shù)據(jù)完全沒(méi)有壓力非對(duì)稱(chēng)計(jì)算主要發(fā)生在握手階段即使純軟件計(jì)算 ECDHE一次握手的開(kāi)銷(xiāo)也就是幾十微秒到幾百微秒的量級(jí)。真正可能拖垮性能的不是 CPU 算力而是握手過(guò)程中的網(wǎng)絡(luò)往返延遲。一個(gè) 200 毫秒延遲的跨地域請(qǐng)求如果每次都要完整握手用戶(hù)體驗(yàn)就是刺眼的卡頓所以前面提到的會(huì)話(huà)復(fù)用和 TLS 1.3 才是性能優(yōu)化的重心。還有一個(gè)容易忽略的點(diǎn)TLS 在握手完成后會(huì)定期重新協(xié)商密鑰也就是會(huì)話(huà)密鑰的更新。這個(gè)重新協(xié)商過(guò)程在舊版協(xié)議里會(huì)帶來(lái)明顯的延遲波動(dòng)現(xiàn)代版本已經(jīng)把它優(yōu)化成了無(wú)中斷的密鑰更新。在服務(wù)器端配置合理的ssl_session_cache加上ssl_session_timeout控制在 1 到 4 小時(shí)絕大多數(shù)連接的握手都能命中緩存。最后提一下硬件加速很多專(zhuān)業(yè)級(jí)的負(fù)載均衡器和網(wǎng)卡都內(nèi)置了非對(duì)稱(chēng)加解密加速模塊高并發(fā)環(huán)境下把私鑰操作卸載到硬件上能省下大量的 CPU 算力。中小型服務(wù)其實(shí)用不著上這個(gè)因?yàn)楹驼w業(yè)務(wù)開(kāi)銷(xiāo)相比TLS 加密所占比例真的不高把配置調(diào)對(duì)遠(yuǎn)比堆硬件重要。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 一串命令解決大部分疑難雜癥平時(shí)排查 HTTPS 問(wèn)題我最常用的不是瀏覽器而是命令行工具因?yàn)槊钚蟹祷氐男畔⒈葹g覽器更直觀而且能控制細(xì)節(jié)。第一個(gè)是openssl s_client。這個(gè)命令可以模擬一個(gè)完整的 TLS 客戶(hù)端連接目標(biāo)服務(wù)器并展示協(xié)議協(xié)商、證書(shū)鏈和握手過(guò)程。openssl s_client -connect example.com:443 -servername example.com -state -debug如果服務(wù)器返回的verify return code不是 0說(shuō)明證書(shū)鏈有問(wèn)題如果輸出的協(xié)議版本是TLSv1或者TLSv1.1說(shuō)明服務(wù)端配置還停留在遠(yuǎn)古版本如果加密套件顯示NULL-MD5或者RC4-SHA這類(lèi)幾乎只在博物館里存在的名字那建議立刻去看服務(wù)端 ssl_ciphers 配置。第二個(gè)是curl -vI https://example.com。-v輸出完整的握手和響應(yīng)過(guò)程-I只請(qǐng)求響應(yīng)頭。注意觀察輸出里有沒(méi)有SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384這一行能快速確認(rèn)實(shí)際協(xié)商出來(lái)的協(xié)議版本和加密套件。第三個(gè)是瀏覽器開(kāi)發(fā)者工具里的 Security 面板。打開(kāi)任意一個(gè)使用 HTTPS 的站點(diǎn)點(diǎn)開(kāi)地址欄的鎖頭圖標(biāo)可以看到證書(shū)詳情、協(xié)議版本和加密套件。這個(gè)途徑適合快速檢查“某個(gè)具體頁(yè)面到底有沒(méi)有真的走 TLS”。還有必不可少的在線(xiàn)掃描服務(wù)比如很多公司采用的網(wǎng)頁(yè)安全評(píng)估平臺(tái)可以輸入域名自動(dòng)檢查 SSL 配置、證書(shū)鏈、協(xié)議兼容性等幾十個(gè)維度給出評(píng)分和修復(fù)建議。這個(gè)適合大型站點(diǎn)的日常巡檢不用手動(dòng)逐臺(tái)跑命令。5.2 排查清單按癥狀對(duì)號(hào)入座整理了我在多處事故現(xiàn)場(chǎng)排查后的常見(jiàn)對(duì)應(yīng)關(guān)系先對(duì)號(hào)入座再動(dòng)手。癥狀可能原因排查手段解法瀏覽器提示“您的連接不是私密連接”證書(shū)不被信任通常是因?yàn)樽院灻蜃C書(shū)鏈不完整點(diǎn)擊“高級(jí)”查看錯(cuò)誤碼用 openssl 查看證書(shū)鏈正確配置證書(shū)文件確保包含中間證書(shū)報(bào)錯(cuò)ERR_CERT_COMMON_NAME_INVALID證書(shū)域名與實(shí)際訪(fǎng)問(wèn)域名不匹配查看證書(shū) SAN 字段和服務(wù)器訪(fǎng)問(wèn)域名核對(duì)重新申請(qǐng)包含目標(biāo)域名的證書(shū)報(bào)錯(cuò)ERR_SSL_VERSION_OR_CIPHER_MISMATCH服務(wù)端協(xié)議版本過(guò)低或客戶(hù)端不支持服務(wù)端加密套件用 s_client 查看服務(wù)端協(xié)議版本在服務(wù)端升級(jí) TLS 1.2并調(diào)整加密套件列表站點(diǎn)頁(yè)面加載緩慢首屏明顯卡頓未開(kāi)啟會(huì)話(huà)復(fù)用或客戶(hù)端驗(yàn)證 OCSP檢查網(wǎng)絡(luò)請(qǐng)求時(shí)間分布確認(rèn)握手耗時(shí)開(kāi)啟會(huì)話(huà)緩存和 OCSP Stapling升級(jí) TLS 1.3部分老客戶(hù)端完全無(wú)法訪(fǎng)問(wèn)協(xié)議或加密套件過(guò)新老設(shè)備不支持確認(rèn)客戶(hù)端的系統(tǒng)版本和支持的協(xié)議對(duì)老客戶(hù)端做條件兼容或引導(dǎo)升級(jí)頁(yè)面出現(xiàn)混合內(nèi)容錯(cuò)誤頁(yè)面靜態(tài)資源仍用 HTTP 加載瀏覽器控制臺(tái)查看具體資源 URL全局替換靜態(tài)資源協(xié)議為 HTTPS這個(gè)表格是我實(shí)際排障過(guò)程的高度濃縮碰到新問(wèn)題時(shí)優(yōu)先按照表格里的維度一一確認(rèn)切勿盲目重啟服務(wù)或者隨意升級(jí)協(xié)議版本。5.3 容易被忽略的三個(gè)冷知識(shí)第一個(gè)是 TLS 指紋。即使流量是加密的但它最初的ClientHello報(bào)文本身是明文里面攜帶的加密套件列表、擴(kuò)展字段、順序、TLS 版本號(hào)組合在一起會(huì)形成一個(gè)特定的指紋。網(wǎng)絡(luò)中的安全設(shè)備可以依據(jù)這個(gè)指紋識(shí)別出你用的具體瀏覽器或設(shè)備型號(hào)也能識(shí)別出某些攻擊工具。應(yīng)用層的這個(gè)特性解釋了為什么某些站點(diǎn)能區(qū)分出普通用戶(hù)和自動(dòng)化腳本事實(shí)上這種基于 TLS 指紋的識(shí)別早已經(jīng)成為風(fēng)控系統(tǒng)的一部分。第二個(gè)是證書(shū)有效期越來(lái)越短。以前一張證書(shū)可以用三年現(xiàn)在全球根證書(shū)程序的新規(guī)是 398 天很多公開(kāi)信任的 CA 開(kāi)始推行 90 天有效期。這背后是安全理念的變化證書(shū)過(guò)期越頻繁被盜的私鑰和錯(cuò)誤的域名信息才能在越短時(shí)間內(nèi)自動(dòng)失效。對(duì)工程來(lái)說(shuō)證書(shū)自動(dòng)化管理從“可以上”變成了“必須上”靠人肉每個(gè)月去換證書(shū)已經(jīng)完全不現(xiàn)實(shí)。這就是 ACME 協(xié)議和自動(dòng)續(xù)期工具大行其道的根本原因。第三個(gè)是 Extended Master Secret 擴(kuò)展簡(jiǎn)稱(chēng) EMS。它通過(guò)把握手中的全部信息納入會(huì)話(huà)密鑰派生過(guò)程預(yù)防了某個(gè)特定類(lèi)型的密鑰泄露攻擊。現(xiàn)在主流庫(kù)默認(rèn)啟用但在某些兼容性配置里關(guān)閉它會(huì)暴露舊的問(wèn)題。所以盡量不要因?yàn)槟硞€(gè)老系統(tǒng)不兼容就去禁用這個(gè)擴(kuò)展優(yōu)先級(jí)應(yīng)當(dāng)優(yōu)先保證安全再想兼容方案。這些冷知識(shí)看起來(lái)小但在做安全評(píng)估或者部署高合規(guī)系統(tǒng)時(shí)很容易成為評(píng)審專(zhuān)家關(guān)注的點(diǎn)提前了解比臨時(shí)補(bǔ)課要輕松得多。關(guān)于排錯(cuò)的兩句實(shí)在話(huà)到現(xiàn)在我已經(jīng)把協(xié)議設(shè)計(jì)、版本演進(jìn)、握手流程、證書(shū)體系和部署優(yōu)化都串完了。在實(shí)際操作層面我想多說(shuō)兩句第一任何 HTTPS 問(wèn)題都別急著改服務(wù)端配置先用工具確認(rèn)“連接有沒(méi)有建立成功、協(xié)議是什么版本、證書(shū)鏈?zhǔn)欠裢暾边@三件事大半問(wèn)題都能定位到范圍第二升級(jí)協(xié)議版本時(shí)要有灰度意識(shí)先在測(cè)試環(huán)境用真實(shí)設(shè)備探一遍再推向生產(chǎn)避免出現(xiàn)新用戶(hù)全好、老用戶(hù)全崩的局面。說(shuō)到底SSL 這份協(xié)議之所以值得細(xì)摳是因?yàn)樗呀?jīng)是整個(gè)互聯(lián)網(wǎng)可信體系的基石。理解了握手和證書(shū)內(nèi)部的邏輯你在排查“小鎖頭變紅”的時(shí)候就不容易慌去配置安全策略的時(shí)候也更有底氣。這些細(xì)節(jié)平時(shí)看文檔容易一帶而過(guò)真正用到的時(shí)候才發(fā)現(xiàn)每一層都少不得。