
第一次在抓包工具里看到自己登錄請求里的賬號密碼以純文本形式躺在那里說實話有點后背發(fā)涼——那條請求穿過了家里的路由器、運營商鏈路、若干個中間設備任何一環(huán)想看一眼都能看得一清二楚。這就是HTTP最原始的樣子它假設鏈路是可信的而現(xiàn)實里鏈路從來不可信。HTTPS存在的意義就是把假設換成驗證。這篇內容我會從一次明文抓包的現(xiàn)場講起把TLS握手里每一步在干什么、證書信任鏈是怎么一層層搭起來的、HTTPS和HTTP在報文語義與工程行為上到底差在哪以及證書部署、Nginx配置、線上排障的完整鏈路都過一遍。適合已經(jīng)會寫接口但沒系統(tǒng)啃過TLS的開發(fā)者也適合正在給站點上證書、被證書鏈不完整混合內容這類報錯卡住的同學。文中所有域名、證書、場景均為示例參數(shù)以主流實踐為準。1. 從一次抓包現(xiàn)場看HTTP到底缺了什么1.1 明文報文在鏈路上到底有多透明HTTP的本質是一段可讀文本請求行、請求頭、請求體依次排開中間用空行分隔。這種設計在早期是優(yōu)點調試方便隨手一個telnet就能發(fā)請求日志里一眼能看出問題。但它同時意味著報文在網(wǎng)絡中每一跳都是裸奔狀態(tài)。家用路由器、公司的上網(wǎng)行為管理設備、機房里的鏡像交換機只要能看到流量就能把報文內容原樣打印出來。很多人以為我訪問的是內網(wǎng)系統(tǒng)沒人看。這個假設成立的前提是鏈路完全可控而現(xiàn)實是只要有一次請求經(jīng)過公共網(wǎng)絡鏈路就不再由你掌控。更隱蔽的是被動記錄流量被留存下來當時沒人看幾個月后被人翻出來分析這類風險在明文協(xié)議下無法消除。所以討論HTTP的安全性第一句話應該是它壓根沒打算提供安全性它只負責把字節(jié)從A搬到B。1.2 三個缺口機密性、完整性、身份真實性把HTTP的問題拆開看其實是三個獨立的缺口而不是一個籠統(tǒng)的不安全。機密性缺失報文可讀賬號、令牌、業(yè)務數(shù)據(jù)全部暴露。這是最容易理解的一個。完整性缺失中間設備不僅能看還能改。你請求的是A頁面返回的可能被插入了一段腳本你提交的金額是100中途可能被改成1000。HTTP本身沒有任何校驗機制來發(fā)現(xiàn)這種篡改接收方拿到什么就信什么。身份真實性缺失你連上的服務器到底是不是你以為的那臺HTTP層面無從判斷。DNS被污染、鏈路被劫持到一臺仿冒服務器客戶端完全感知不到因為它沒有對方是誰這個概念。這三個缺口是并列關系不是遞進關系。這一點很關鍵——很多人在做方案時會誤以為我加了加密就安全了但實際上只做加密不做身份驗證等于把保險箱交給一個沒核對身份證的快遞員他拿著鑰匙一樣能開。1.3 只加密不驗身份等于把保險箱交給陌生人舉個具體例子說明這個誤區(qū)。假設客戶端和服務器之間用了一個雙方共享的對稱密鑰做加密鏈路中間人不知道密鑰看起來安全了。但問題是這個密鑰怎么送到對方手上如果明文送中間人截獲即可如果提前內置在客戶端里那所有客戶端共用一把鑰匙泄露一次全盤皆輸如果每次協(xié)商那協(xié)商過程本身又需要保護——這是一個循環(huán)依賴。非對稱加密打破了這個循環(huán)但它帶來新問題客戶端拿到的公鑰怎么確認是服務器的而不是中間人偽造的答案就是證書體系。證書解決的不是怎么加密而是我手上這把公鑰屬于誰。所以完整的HTTPS 加密解決機密性 消息認證碼解決完整性 證書驗證解決身份真實性三者缺一不可。只做了前兩個而跳過第三個的方案在真實攻擊面前基本等于沒做。2. 一次HTTPS連接里TLS握手究竟在做什么2.1 協(xié)商階段ClientHello與ServerHello談了什么TLS握手的第一步是雙方對暗號確認彼此支持哪些能力。客戶端發(fā)出ClientHello里面裝了幾樣東西支持的TLS版本列表、密碼套件列表Cipher Suites、一個客戶端隨機數(shù)Client Random、SNI擴展告訴服務器我要訪問哪個域名、支持的橢圓曲線和簽名算法TLS 1.3里還會直接帶上密鑰交換的公開參數(shù)key_share。服務器回ServerHello從客戶端給的列表里挑一套雙方都支持的組合回一個服務器隨機數(shù)Server Random并確認最終使用的TLS版本和密碼套件。這里有個容易被忽略的點密碼套件是服務器選的但候選范圍是客戶端給的。如果客戶端只提供了老舊套件服務器再想用強套件也沒得選。這也是為什么老客戶端會拖累整體安全基線。SNI這個擴展值得單獨說。一臺服務器上跑幾十個站點共用同一個IP時服務器需要在握手階段就知道客戶端要訪問哪個域名才能選對應的證書。SNI就是在ClientHello里明文傳的域名——注意握手階段它還沒加密所以在一些對隱私要求高的場景里會引入加密的ClientHello方案但那是后話主流部署里SNI依然是明文。2.2 驗證階段瀏覽器拿到證書后做的五件事服務器把證書鏈發(fā)過來之后客戶端要做一系列檢查任何一項不通過都會亮紅燈。這套檢查邏輯值得逐條記住因為線上排障時90%的證書問題都出在這五步里。第一鏈式驗證。從服務器發(fā)來的站點證書開始一級一級往上找簽發(fā)者直到找到一個客戶端本地信任庫里存在的根證書。鏈條斷在任何一環(huán)驗證就失敗。第二簽名驗證。每一級證書的簽名都要用上一級的公鑰去驗。這一步保證了證書內容沒有被篡改過。第三有效期檢查。當前時間必須落在證書的notBefore和notAfter之間。這里有個隱藏坑客戶端系統(tǒng)時間不準會導致本來有效的證書被判過期報錯信息還非常具有誤導性。第四域名匹配。檢查證書的SANSubject Alternative Name擴展里是否包含客戶端請求的域名。注意現(xiàn)代瀏覽器已經(jīng)完全忽略CN字段只看SAN。通配符證書*.example.com只匹配一級子域a.b.example.com是匹配不上的。第五用途與吊銷狀態(tài)。證書的擴展密鑰用法EKU里必須包含serverAuth吊銷狀態(tài)通過CRL或OCSP查詢OCSP Stapling就是把查詢結果由服務器緩存后隨握手一起下發(fā)省掉客戶端的一次額外請求。2.3 密鑰交換為什么最后一定要落到對稱加密非對稱加密RSA、ECDSA的運算成本比對稱加密高出幾個數(shù)量級拿它來加密業(yè)務數(shù)據(jù)是不現(xiàn)實的。所以TLS的做法是用非對稱手段安全地協(xié)商出一份對稱密鑰然后整個會話的數(shù)據(jù)都用這份對稱密鑰加密。以ECDHE為例雙方各自生成一對臨時密鑰交換公開部分各自用自己的私鑰和對方的公鑰算出同一個共享秘密再經(jīng)過HKDF之類的密鑰派生函數(shù)從這個共享秘密里推導出會話用的對稱密鑰、初始向量等參數(shù)。這個過程的妙處在于臨時私鑰用完即棄即使服務器的長期私鑰日后泄露攻擊者也無法解密過去記錄的流量——這就是前向保密Forward Secrecy。TLS 1.3把這一點做到了極致它直接砍掉了不支持前向保密的RSA密鑰交換只保留(EC)DHE和PSK兩類模式。所以如果你還在用TLS 1.2并配置了RSA密鑰交換套件實際上是在給自己留一個歷史流量被解密的隱患。2.4 TLS 1.2與1.3的握手差距在哪兩者的差距不只是版本號加一握手輪次直接少了一半。TLS 1.2的完整握手需要2個RTT客戶端發(fā)ClientHello第1次往返開始服務器回ServerHelloCertificateServerKeyExchangeServerHelloDone第1次往返結束客戶端發(fā)ClientKeyExchangeChangeCipherSpecFinished第2次往返開始服務器回ChangeCipherSpecFinished第2次往返結束。這之后才能開始傳應用數(shù)據(jù)。TLS 1.3把密鑰交換的參數(shù)提前塞進ClientHello的key_share擴展服務器收到后立刻就能算出共享秘密于是只需要1個RTTClientHello發(fā)出服務器回ServerHello此時已經(jīng)加密Finished握手完成應用數(shù)據(jù)緊跟著就能發(fā)。更激進的是0-RTT模式客戶端用之前會話留下的PSK在第一個包里就帶上應用數(shù)據(jù)——但0-RTT有重放風險只適合冪等請求絕對不能拿它傳轉賬下單這類操作。對比項TLS 1.2TLS 1.3完整握手RTT21會話恢復Session ID / Session TicketPSK支持0-RTT密鑰交換RSA 或 (EC)DHE僅(EC)DHE / PSK對稱算法含CBC模式僅AEADGCM/ChaCha20-Poly1305/CCM握手明文部分證書明文傳輸除ClientHello外基本加密這張表里證書明文傳輸這一行值得注意TLS 1.2下服務器證書在握手中是明文下發(fā)的旁觀者能看到你訪問的是哪個站點雖然看不到內容TLS 1.3把ServerHello之后的內容全部加密隱私性更好。3. 證書信任鏈那把小鎖背后的層級關系3.1 根證書、中間證書與信任錨證書體系是一棵樹。最頂端是根證書它自簽名預置在操作系統(tǒng)和瀏覽器的信任庫里。根證書的私鑰被極度嚴密地保管幾乎不直接簽發(fā)站點證書。中間層是中間證書由根證書簽發(fā)站點證書則由中間證書簽發(fā)。這樣一個三級結構根 → 中間 → 站點是當前的主流。為什么要多一層中間證書因為根證書一旦泄露吊銷成本極高——它內置在幾十億臺設備里沒法快速更新。引入中間層之后根私鑰可以離線保存日常簽發(fā)全部由中間證書完成出問題只需要吊銷中間證書影響面可控。這個設計思路和很多系統(tǒng)的權限分層是一個道理高頻操作使用低權限憑證高權限憑證盡量少用。客戶端驗證時需要拿到站點證書 中間證書一路追到本地信任庫里的根證書。注意服務器一般不需要下發(fā)根證書因為客戶端本來就有下發(fā)根證書反而會增加握手包體積。3.2 DV、OV、EV與通配符證書怎么選按驗證強度分證書大致三類。DV域名驗證只驗證你對域名的控制權通常通過DNS記錄或文件校驗完成簽發(fā)快、成本低個人站和小型項目基本都選這個。OV組織驗證額外核驗申請主體的真實身份信息簽發(fā)需要幾個工作日適合對外提供服務的商業(yè)站點。EV擴展驗證曾經(jīng)會在瀏覽器地址欄顯示特殊標識現(xiàn)在主流瀏覽器已經(jīng)弱化這個展示實際收益不如從前。按覆蓋范圍分單域名證書只管一個域名通配符證書*.example.com覆蓋同一級的所有子域但不覆蓋裸域example.com本身也不覆蓋多級子域多域名證書SAN證書可以在一張證書里塞入多個不同域名適合域名比較分散的場景。選型時我的判斷順序是先看域名結構再看驗證等級。如果子域很多且都是同一級api、cdn、img、static……通配符最省事續(xù)期也只需要管一張如果域名橫跨多個主域多域名證書更合適如果只是單個小站DV單域名就夠了沒必要為OV的核驗流程多等幾天。有一個細節(jié)容易被忽略通配符證書一旦私鑰泄露所有子域同時受影響所以密鑰管理要更嚴格。3.3 證書鏈配錯是最容易踩的坑我見過最多的HTTPS故障不是證書本身有問題而是服務器只下發(fā)了站點證書沒下發(fā)中間證書。現(xiàn)象很典型用桌面瀏覽器訪問一切正常但用某些移動端App、某些命令行工具、某些嵌入式設備訪問就報證書錯誤。原因在于桌面瀏覽器有證書緩存和AI A中間證書自動補全機制它會自己去補缺失的中間證書而那些沒有這個能力的客戶端就只能看到一條斷掉的鏈。排查方法很直接用openssl看服務器實際下發(fā)的證書數(shù)量openssl s_client -connect example.com:443 -servername example.com -showcerts如果輸出里只有一段-----BEGIN CERTIFICATE-----那基本可以確認缺中間證書。正確做法是把站點證書和中間證書按順序拼成一個fullchain文件順序是站點證書在前中間證書在后順序顛倒同樣會失敗。cat server.crt intermediate.crt fullchain.crt注意拼接時不要包含根證書也不要用中間證書在前的順序。這個細節(jié)在很多教程里被寫錯實測會導致部分客戶端驗證失敗。4. HTTPS與HTTP的差異不止多了一把鎖4.1 連接語義與報文封裝的差異最直觀的差異是端口和封裝。HTTP默認80端口報文直接跑在TCP之上HTTPS默認443端口HTTP報文被整體塞進TLS記錄層再由TLS跑在TCP上。所以在抓包工具里看HTTPS流量只能看到TLS握手和應用數(shù)據(jù)的密文看不到HTTP請求行。這帶來一系列工程上的連鎖反應。比如中間設備不再能解析內容企業(yè)的上網(wǎng)行為管理、運營商的流量整形、某些對HTTP頭做改寫的代理在HTTPS面前統(tǒng)統(tǒng)失效。這既是好事隱私得到保護也是麻煩運維可觀測性下降。所以生產(chǎn)環(huán)境通常會引入TLS終止層——在負載均衡或反向代理上解密內部再用明文或重新加密轉發(fā)把解密點收斂到可控的少數(shù)幾個位置。另一個差異在連接復用上。HTTP/1.1的keep-alive讓一個TCP連接承載多個請求但TLS連接建立本身有額外開銷所以連接池的參數(shù)最大連接數(shù)、空閑超時在HTTPS場景下需要重新調。一個常見的誤配是連接池開得太大導致握手次數(shù)暴漲反而比復用少量長連接更慢。4.2 緩存、代理與重定向的行為差異HTTP的中間緩存可以放心地緩存響應因為內容對所有人一樣。HTTPS下共享緩存CDN、反向代理要緩存內容就必須在某一層解密緩存的是解密后的內容再對每個客戶端單獨重新加密。這決定了緩存層的位置和架構。更細的差異體現(xiàn)在降級場景。當頁面是HTTPS而其中某個資源是HTTP時瀏覽器會判定為混合內容。被動混合內容圖片、音視頻在部分瀏覽器里會被自動升級為HTTPS嘗試失敗則阻止主動混合內容腳本、樣式表、iframe、XHR則直接被阻止加載。這是HTTPS站點最常見的頁面莫名缺東西的原因。Referer的行為也不同。從HTTPS頁面跳到HTTP頁面時默認策略下瀏覽器不會發(fā)送Referer頭這是為了防止把HTTPS頁面的路徑信息泄露到明文鏈路。如果你的業(yè)務依賴Referer做來源統(tǒng)計或防盜鏈跨協(xié)議跳轉時數(shù)據(jù)會丟失這個坑在遷移過程中經(jīng)常被忽略。Cookie的Secure屬性只在HTTPS下才會被發(fā)送SameSite策略的默認值在新版瀏覽器里已經(jīng)收緊為Lax。所以把站點從HTTP遷到HTTPS時Cookie相關的行為變化需要單獨回歸測試一遍。4.3 現(xiàn)代協(xié)議能力只在HTTPS上開放這是很多人沒意識到的差異一批現(xiàn)代Web能力直接把HTTPS設為準入門檻。HTTP/2在瀏覽器里只對TLS連接啟用通過ALPN擴展在握手階段協(xié)商出h2。雖然協(xié)議規(guī)范里存在明文版本的h2c但主流瀏覽器根本不支持。這意味著不上HTTPS你就用不上多路復用、頭部壓縮這些HTTP/2帶來的性能紅利。HTTP/3更徹底它跑在QUIC之上UDP 443端口而QUIC內置了TLS 1.3加密不是可選項而是協(xié)議的一部分。再往前看Service Worker、地理位置、攝像頭麥克風訪問、剪貼板寫入、Web Push、部分PWA能力全部要求在安全上下文Secure Context中運行。所謂安全上下文判定標準就是頁面是否通過HTTPS加載localhost除外。所以HTTPS在今天已經(jīng)不是一個安全加固項而是功能準入項。維度HTTPHTTPS默認端口80443報文可見性明文可讀加密完整性校驗無AEAD/MAC服務端身份驗證無證書鏈驗證HTTP/2支持瀏覽器不支持ALPN協(xié)商啟用HTTP/3支持不支持QUIC內置TLS 1.3安全上下文否是跨協(xié)議Referer正常發(fā)送降級時不發(fā)送中間緩存可直接緩存需在解密層緩存5. TLS的成本賬與優(yōu)化手段5.1 握手延遲與CPU開銷的真實量級經(jīng)常聽到HTTPS會變慢這個說法需要拆開看。延遲方面TLS 1.3完整握手增加1個RTT如果客戶端和服務器之間RTT是30ms那就是多30ms。相比之下DNS解析、TCP三次握手本身也是1個RTT所以首次連接的額外開銷大約是整體連接建立的30%到50%。但連接復用之后這個開銷就攤薄了。CPU方面現(xiàn)代CPU普遍帶有AES-NI指令集對稱加密的吞吐可以做到幾個GB每秒業(yè)務代碼自己產(chǎn)生的開銷往往比加解密大得多。真正貴的是非對稱運算一次ECDSA簽名驗證在毫秒量級如果用RSA 2048簽名也差不多。所以一個每秒處理上萬次新建連接的網(wǎng)關才需要認真考慮握手CPU成本普通業(yè)務站點的瓶頸基本不在TLS上。我的經(jīng)驗是首屏慢不一定是TLS的鍋但握手次數(shù)多一定是架構問題。如果你發(fā)現(xiàn)QPS不高但CPU吃緊先去看是不是每次請求都新建了TLS連接而不是先去調密碼套件。5.2 會話復用、OCSP Stapling與TLS 1.3優(yōu)化手段有幾個層次成本從低到高。會話復用是第一優(yōu)先。TLS 1.2下靠Session ID或Session Ticket讓客戶端在后續(xù)連接中跳過完整的密鑰交換TLS 1.3下統(tǒng)一用PSK。配置上主要是開啟會話緩存并設置合理的超時ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; ssl_session_tickets off;這里10m大約能存4萬個會話1MB約存4000個中小站點足夠用了。ssl_session_tickets off是個安全權衡會話票據(jù)用服務器密鑰加密后交給客戶端保存如果密鑰長期不輪換歷史流量有被解密的風險。關掉票據(jù)、只用服務端緩存安全性更好代價是集群環(huán)境下需要共享緩存。OCSP Stapling解決的是證書吊銷狀態(tài)查詢的延遲問題。不開的話客戶端可能要多發(fā)一次請求去CA查詢鏈路差的時候這一下能拖幾百毫秒。開啟后由服務器定期去查詢并緩存結果握手時直接下發(fā)給客戶端ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/nginx/ssl/chain.pem; resolver 8.8.8.8 valid300s;注意resolver必須配置否則nginx無法解析OCSP服務器地址stapling會靜默失敗。TLS 1.3是收益最大的一步直接把握手砍到一個RTT同時砍掉了老舊的算法。代價是要確認客戶端的兼容性——目前主流瀏覽器和現(xiàn)代操作系統(tǒng)都支持但如果你的用戶里有大量老舊Android設備或嵌入式終端可能需要同時保留TLS 1.2。5.3 證書與協(xié)議參數(shù)的取舍參數(shù)配置上有個常見的過度優(yōu)化傾向把密碼套件列表寫得極長把各種強算法全列上。實際上列表長度對手握延遲的影響很小真正影響兼容性的是最低版本和是否禁用某些降級套件。我的建議基線是最低TLS 1.2優(yōu)先TLS 1.3禁用所有CBC模式套件有padding oracle類攻擊的歷史禁用RSA密鑰交換無前向保密禁用3DES、RC4、MD5、SHA-1。這樣一套下來兼容性覆蓋絕大多數(shù)現(xiàn)代客戶端安全性也站得住。證書算法選擇上ECDSA P-256的證書體積更小、握手更快在有條件的情況下優(yōu)先選它但要注意極老的客戶端可能不支持需要準備RSA證書做回退。雙證書部署ECDSA RSA在nginx里可以通過兩個ssl_certificate指令實現(xiàn)nginx會自動根據(jù)客戶端能力選擇。6. 從申請證書到Nginx上線的一套可復現(xiàn)流程6.1 生成密鑰與CSR第一步是在服務器上生成私鑰和證書簽名請求CSR。私鑰必須留在自己手里絕不能在網(wǎng)絡上傳輸。推薦用ECDSAopenssl ecparam -genkey -name prime256v1 -out privkey.pem openssl req -new -key privkey.pem -out server.csr \ -subj /CCN/STState/LCity/OExample/CNexample.com \ -addext subjectAltNameDNS:example.com,DNS:www.example.com如果目標環(huán)境需要RSA換成openssl req -new -newkey rsa:2048 -nodes \ -keyout privkey.pem -out server.csr \ -subj /CNexample.com \ -addext subjectAltNameDNS:example.com,DNS:www.example.com這里有兩個細節(jié)。-nodes表示私鑰不加密no DES因為nginx啟動時無法交互輸入密碼加密的私鑰需要每次手動解密生產(chǎn)環(huán)境一般不用。subjectAltName必須顯式寫上所有要用到的域名包括裸域和www因為現(xiàn)代客戶端只看SAN。少寫一個域名上線后就是一次證書名稱不匹配的報障。把CSR提交給證書頒發(fā)機構后DV證書通常幾分鐘內就能完成域名驗證并下發(fā)。下載下來一般是一個壓縮包里面包含站點證書、中間證書有的還有根證書。這時候按前面說的規(guī)則拼fullchaincat server.crt intermediate.crt fullchain.pem6.2 Nginx關鍵配置逐行解釋下面這份配置是我目前用得比較順手的一套基線每一項都標注了理由server { listen 443 ssl; http2 on; # nginx 1.25.1 寫法舊版本用 listen 443 ssl http2; server_name example.com www.example.com; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; 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:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_prefer_server_ciphers off; # TLS 1.3時代交給客戶端選更合理 ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/nginx/ssl/chain.pem; resolver 8.8.8.8 1.1.1.1 valid300s; resolver_timeout 5s; add_header Strict-Transport-Security max-age31536000; includeSubDomains always; add_header X-Content-Type-Options nosniff always; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; } }幾個必須解釋的點。ssl_prefer_server_ciphers off看著反直覺但在TLS 1.3下密碼套件已經(jīng)由客戶端和服務端的交集決定強行讓服務端優(yōu)先沒有實際收益反而可能選到客戶端不太擅長的算法。X-Forwarded-Proto $scheme這一行非常關鍵——后端應用需要知道原始請求是HTTP還是HTTPS否則生成的重定向地址、靜態(tài)資源鏈接可能還是http開頭直接引發(fā)混合內容。HTTP到HTTPS的跳轉單獨寫一個server塊不要用ifserver { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; }return 301比rewrite效率高語義也更明確。注意這里用$host而不是寫死域名避免多域名場景下跳錯。HSTS頭要謹慎開啟。max-age31536000意味著一整年內瀏覽器都會強制用HTTPS訪問該域名一旦證書出問題用戶連繼續(xù)訪問的按鈕都點不了。我的做法是先用一個很短的max-age比如300秒跑一周確認沒有問題再逐步加長。6.3 上線后的驗證清單配完不是重啟就完事至少過一遍這幾項。檢查協(xié)議版本openssl s_client -connect example.com:443 -servername example.com -tls1_3 /dev/null 21 | grep Protocol確認TLS 1.3可用同時試著用-tls1連接應該失敗。檢查證書鏈完整性openssl s_client -connect example.com:443 -servername example.com -showcerts 2/dev/null | grep -c BEGIN CERTIFICATE正常應該輸出2站點證書 中間證書。檢查證書到期時間echo | openssl s_client -connect example.com:443 -servername example.com 2/dev/null | openssl x509 -noout -dates檢查HTTP/2是否生效curl -I --http2 https://example.com響應行里應該出現(xiàn)HTTP/2。檢查混合內容打開瀏覽器開發(fā)者工具切到Console和Network面板把所有頁面走一遍看有沒有被阻止的資源。這一步最容易被跳過但問題往往在這里。7. 線上排障幾個高頻故障的定位鏈路7.1 部分客戶端報證書錯誤部分正常這個現(xiàn)象最典型的根因就是證書鏈不完整。診斷路徑是先用openssl s_client -showcerts看服務器下發(fā)了幾張證書如果只有一張基本可以確定。修復方法是補上中間證書并重載nginx。還有一種變體鏈完整但中間證書順序錯了。有些CA下發(fā)的壓縮包里同時包含交叉簽名證書和普通中間證書如果拼錯順序部分客戶端會驗證失敗。判斷方法是把服務器下發(fā)的證書逐個拆分出來用openssl verify逐個驗證路徑。第三個可能是SNI相關。極老的客戶端某些老舊Android版本、部分Java 6/7環(huán)境握手時不發(fā)SNI服務器只能返回默認證書如果默認證書不覆蓋該域名就會報名稱不匹配。這種情況下要么升級客戶端要么讓默認證書覆蓋主域名。第四個可能是客戶端時間不對。嵌入式設備的系統(tǒng)時間停在幾年前驗證有效期時直接失敗。這個從服務端日志里能看出端倪請求集中在某幾類User-Agent上且這些設備的TLS握手版本往往也比較老。7.2 混合內容與HSTS導致的打不開混合內容的排查思路是先看Console里報的阻塞類型。主動混合內容腳本、樣式、iframe是被硬阻止的Console會明確報Mixed Content: The page at https://... was loaded over HTTPS, but requested an insecure ...。被動混合內容圖片通常是警告部分瀏覽器會自動升級。修復方式有三種按優(yōu)先級排把資源地址改成相對路徑或協(xié)議相對路徑現(xiàn)在更推薦直接用https絕對路徑在頁面里加meta http-equivContent-Security-Policy contentupgrade-insecure-requests讓瀏覽器自動升級實在改不動的第三方資源就在服務端做一層代理轉發(fā)。HSTS導致的問題更隱蔽。一旦瀏覽器記錄了這個域名的HSTS策略之后所有HTTP請求在瀏覽器內部就被改寫成HTTPS用戶完全無感但如果證書失效或服務掛掉用戶會看到無法跳過的警告頁。排查時注意HSTS記錄在瀏覽器本地清緩存、換瀏覽器、用無痕模式都可以用來確認是不是HSTS導致的。運維層面如果確實需要緊急降級只能等max-age過期或者讓用戶手動清理瀏覽器HSTS設置——這就是為什么前面強調max-age要循序漸進。7.3 證書過期與時間不同步證書過期是最不該發(fā)生但又最常發(fā)生的一類故障。根本原因是缺乏自動化的到期監(jiān)控。人工記日歷遲早會漏尤其當證書數(shù)量上來之后??尚械淖龇ㄊ窃诒O(jiān)控系統(tǒng)里加一個探針每天檢查證書剩余天數(shù)低于30天告警低于7天升級告警級別。檢查邏輯很簡單就是前面那條openssl命令加日期解析。時間不同步的問題在容器環(huán)境里更常見。容器啟動時如果宿主機時間不準或者NTP服務沒配好證書驗證會莫名其妙失敗。這類問題的排查要養(yǎng)成一個習慣看到證書報錯第一反應先確認時間和時區(qū)。一條date -u就能排除掉一大類問題。還有一個容易踩的坑是自動續(xù)期任務靜默失敗。續(xù)期腳本跑在cron里失敗了沒人知道等到證書真的過期才炸。所以續(xù)期任務必須配置失敗告警并且在續(xù)期成功后自動reload服務。reload這一步經(jīng)常被漏掉——證書文件更新了但nginx還在用內存里的舊證書表面上文件是新的實際服務下發(fā)的還是舊的。提示續(xù)期成功后務必執(zhí)行nginx -t nginx -s reload??梢韵扔胣ginx -t檢查配置通過了再reload避免因為配置問題導致服務中斷。我個人在這塊踩過的坑是早年覺得證書配好就完事結果第一次到期是在一個周五晚上站點直接不可訪問用戶投訴才發(fā)現(xiàn)。從那之后我的做法是任何證書上線時同步做三件事——加監(jiān)控、配續(xù)期、續(xù)期后自動reload三件缺一件都算沒做完。另外一個小技巧是把證書的到期時間寫進巡檢腳本的輸出里每周看一眼比任何告警都讓人踏實。TLS這套東西真正難的不是理解握手流程而是把證書的生命周期管理做成一個不需要人盯著的閉環(huán)這一點做過幾年運維的人應該都有體會。