如何做虛擬幣交易網(wǎng)站外貿(mào)推廣如何做
四、安全
????????1、TLS是什么
??????(1)為什么要有HTTPS
????????? 簡(jiǎn)單的回答是“因?yàn)?HTTP 不安全”。由于 HTTP 天生“明文”的特點(diǎn),整個(gè)傳輸過(guò)程完全透明,任何人都能夠在鏈路中截獲、修改或者偽造請(qǐng)求 / 響應(yīng)報(bào)文,數(shù)據(jù)不具有可信性。
????????比如,前幾講中說(shuō)過(guò)的“代理服務(wù)”。它作為 HTTP 通信的中間人,在數(shù)據(jù)上下行的時(shí)候可以添加或刪除部分頭字段,也可以使用黑白名單過(guò)濾 body 里的關(guān)鍵字,甚至直接發(fā)送虛假的請(qǐng)求、響應(yīng),而瀏覽器和源服務(wù)器都沒(méi)有辦法判斷報(bào)文的真?zhèn)巍?br /> ????????這對(duì)于網(wǎng)絡(luò)購(gòu)物、網(wǎng)上銀行、證券交易等需要高度信任的應(yīng)用場(chǎng)景來(lái)說(shuō)是非常致命的。如果沒(méi)有基本的安全保護(hù),使用互聯(lián)網(wǎng)進(jìn)行各種電子商務(wù)、電子政務(wù)就根本無(wú)從談起。
????????對(duì)于安全性要求不那么高的新聞、視頻、搜索等網(wǎng)站來(lái)說(shuō),由于互聯(lián)網(wǎng)上的惡意用戶、惡意代理越來(lái)越多,也很容易遭到“流量劫持”的攻擊,在頁(yè)面里強(qiáng)行嵌入廣告,或者分流用戶,導(dǎo)致各種利益損失。
????????對(duì)于你我這樣的普通網(wǎng)民來(lái)說(shuō),HTTP 不安全的隱患就更大了,上網(wǎng)的記錄會(huì)被輕易截獲,網(wǎng)站是否真實(shí)也無(wú)法驗(yàn)證,黑客可以偽裝成銀行網(wǎng)站,盜取真實(shí)姓名、密碼、銀行卡等敏感信息,威脅人身安全和財(cái)產(chǎn)安全。
????????(2)什么是安全
????????通常認(rèn)為,如果通信過(guò)程具備了四個(gè)特性,就可以認(rèn)為是“安全”的,這四個(gè)特性是:機(jī)密性、完整性,身份認(rèn)證和不可否認(rèn)。
- 機(jī)密性(Secrecy/Confidentiality)是指對(duì)數(shù)據(jù)的“保密”,只能由可信的人訪問(wèn),對(duì)其他人是不可見(jiàn)的“秘密”,簡(jiǎn)單來(lái)說(shuō)就是不能讓不相關(guān)的人看到不該看的東西。
- 完整性(Integrity,也叫一致性)是指數(shù)據(jù)在傳輸過(guò)程中沒(méi)有被竄改,不多也不少,“完完整整”地保持著原狀。
- 身份認(rèn)證(Authentication)是指確認(rèn)對(duì)方的真實(shí)身份,也就是“證明你真的是你”,保證消息只能發(fā)送給可信的人。
- 不可否認(rèn)(Non-repudiation/Undeniable),也叫不可抵賴,意思是不能否認(rèn)已經(jīng)發(fā)生過(guò)的行為,不能“說(shuō)話不算數(shù)”“耍賴皮”。
????????(3)什么是HTTPS
????????HTTPS為 HTTP 增加了四大安全特性。HTTPS 其實(shí)是一個(gè)“非常簡(jiǎn)單”的協(xié)議,RFC 文檔很小,只有短短的 7 頁(yè),里面規(guī)定了新的協(xié)議名“https”,默認(rèn)端口號(hào) 443,至于其他的什么請(qǐng)求 - 應(yīng)答模式、報(bào)文結(jié)構(gòu)、請(qǐng)求方法、URI、頭字段、連接管理等等都完全沿用 HTTP,沒(méi)有任何新的東西。
?????????HTTPS 把 HTTP 下層的傳輸協(xié)議由 TCP/IP 換成了SSL/TLS,由“HTTP over TCP/IP”變成了“HTTP over SSL/TLS”,讓 HTTP 運(yùn)行在了安全的 SSL/TLS 協(xié)議上,收發(fā)報(bào)文不再使用 Socket API,而是調(diào)用專門(mén)的安全接口。
????????? 所以說(shuō),HTTPS 本身并沒(méi)有什么特別,全是靠下層的SSL/TLS。
????????(4)SSL/TLS
????????SSL 即安全套接層(Secure Sockets Layer),在 OSI 模型中處于第 5 層(會(huì)話層),由網(wǎng)景公司于 1994 年發(fā)明,有 v2 和 v3 兩個(gè)版本,而 v1 因?yàn)橛袊?yán)重的缺陷從未公開(kāi)過(guò)。
????????SSL 發(fā)展到 v3 時(shí)已經(jīng)證明了它自身是一個(gè)非常好的安全通信協(xié)議,于是互聯(lián)網(wǎng)工程組 IETF在 1999 年把它改名為 TLS(傳輸層安全,Transport Layer Security),正式標(biāo)準(zhǔn)化,版本號(hào)從 1.0 重新算起,所以 TLS1.0 實(shí)際上就是 SSLv3.1。
????????到今天 TLS 已經(jīng)發(fā)展出了三個(gè)版本,分別是 2006 年的 1.1、2008 年的 1.2 和去年(2018)的 1.3,每個(gè)新版本都緊跟密碼學(xué)的發(fā)展和互聯(lián)網(wǎng)的現(xiàn)狀,持續(xù)強(qiáng)化安全和性能,已經(jīng)成為了信息安全領(lǐng)域中的權(quán)威標(biāo)準(zhǔn)。
????????目前應(yīng)用的最廣泛的 TLS 是 1.2,而之前的協(xié)議(TLS1.1/1.0、SSLv3/v2)都已經(jīng)被認(rèn)為是不安全的,各大瀏覽器即將在 2020 年左右停止支持,所以接下來(lái)的講解都針對(duì)的是TLS1.2。
????????TLS 由記錄協(xié)議、握手協(xié)議、警告協(xié)議、變更密碼規(guī)范協(xié)議、擴(kuò)展協(xié)議等幾個(gè)子協(xié)議組成,綜合使用了對(duì)稱加密、非對(duì)稱加密、身份認(rèn)證等許多密碼學(xué)前沿技術(shù)。
????????瀏覽器和服務(wù)器在使用 TLS 建立連接時(shí)需要選擇一組恰當(dāng)?shù)募用芩惴▉?lái)實(shí)現(xiàn)安全通信,這些算法的組合被稱為“密碼套件”(cipher suite,也叫加密套件)。
????????TLS 的密碼套件命名非常規(guī)范,格式很固定?;镜男问绞恰懊荑€交換算法 + 簽名算法 + 對(duì)稱加密算法 + 摘要算法”,比如ECDHE-RSA-AES256-GCM-SHA384的意思就是:“握手時(shí)使用 ECDHE 算法進(jìn)行密鑰交換,用 RSA 簽名和身份認(rèn)證,握手后的通信使用AES 對(duì)稱算法,密鑰長(zhǎng)度 256 位,分組模式是 GCM,摘要算法 SHA384 用于消息認(rèn)證和產(chǎn)生隨機(jī)數(shù)?!?/p>
????????(5)OpenSSL
????????? OpenSSL,它是一個(gè)著名的開(kāi)源密碼學(xué)程序庫(kù)和工具包,幾乎支持所有公開(kāi)的加密算法和協(xié)議,已經(jīng)成為了事實(shí)上的標(biāo)準(zhǔn),許多應(yīng)用軟件都會(huì)使用它作為底層庫(kù)來(lái)實(shí)現(xiàn) TLS 功能,包括常用的 Web 服務(wù)器 Apache、Nginx 等。
????????OpenSSL 是從另一個(gè)開(kāi)源庫(kù) SSLeay 發(fā)展出來(lái)的,曾經(jīng)考慮命名為“OpenTLS”,但當(dāng)時(shí)(1998 年)TLS 還未正式確立,而 SSL 早已廣為人知,所以最終使用了“OpenSSL”的名字。
????????由于 OpenSSL 是開(kāi)源的,所以它還有一些代碼分支,比如 Google 的 BoringSSL、OpenBSD 的 LibreSSL,這些分支在 OpenSSL 的基礎(chǔ)上刪除了一些老舊代碼,也增加了一些新特性,雖然背后有“大金主”,但離取代 OpenSSL 還差得很遠(yuǎn)。
????????2、對(duì)稱加密與非對(duì)稱加密
????????? 加密:實(shí)現(xiàn)機(jī)密性最常用的手段是加密(encrypt),就是把消息用某種方式轉(zhuǎn)換成誰(shuí)也看不懂的亂碼,只有掌握特殊“鑰匙”的人才能再轉(zhuǎn)換出原始文本。
?????????這里的“鑰匙”就叫做“密鑰”(key),加密前的消息叫“明文”(plain text/clear text),加密后的亂碼叫“密文”(cipher text),用密鑰還原明文的過(guò)程叫“解密”(decrypt),是加密的反操作,加密解密的操作過(guò)程就是“加密算法”。
????????所有的加密算法都是公開(kāi)的,任何人都可以去分析研究,而算法使用的“密鑰”則必須保密。
????????由于 HTTPS、TLS 都運(yùn)行在計(jì)算機(jī)上,所以“密鑰”就是一長(zhǎng)串的數(shù)字,但約定俗成的度量單位是“位”(bit),而不是“字節(jié)”(byte)。比如,說(shuō)密鑰長(zhǎng)度是 128,就是 16字節(jié)的二進(jìn)制串,密鑰長(zhǎng)度 1024,就是 128 字節(jié)的二進(jìn)制串。
????????按照密鑰的使用方式,加密可以分為兩大類:對(duì)稱加密和非對(duì)稱加密。
????????(1)對(duì)稱加密
? ? ? ? ?對(duì)稱加密:就是指加密和解密時(shí)使用的密鑰都是同一個(gè),是“對(duì)稱”的。只要保證了密鑰的安全,那整個(gè)通信過(guò)程就可以說(shuō)具有了機(jī)密性。
????????舉個(gè)例子,你想要登錄某網(wǎng)站,只要事先和它約定好使用一個(gè)對(duì)稱密碼,通信過(guò)程中傳輸?shù)娜怯妹荑€加密后的密文,只有你和網(wǎng)站才能解密。黑客即使能夠竊聽(tīng),看到的也只是亂碼,因?yàn)闆](méi)有密鑰無(wú)法解出明文,所以就實(shí)現(xiàn)了機(jī)密性。
?????????TLS 里有非常多的對(duì)稱加密算法可供選擇,比如 RC4、DES、3DES、AES、ChaCha20等,但前三種算法都被認(rèn)為是不安全的,通常都禁止使用,目前常用的只有 AES 和ChaCha20。
????????AES :意思是“高級(jí)加密標(biāo)準(zhǔn)”(Advanced Encryption Standard),密鑰長(zhǎng)度可以是128、192 或 256。它是 DES 算法的替代者,安全強(qiáng)度很高,性能也很好,而且有的硬件還會(huì)做特殊優(yōu)化,所以非常流行,是應(yīng)用最廣泛的對(duì)稱加密算法。
????????ChaCha20:是 Google 設(shè)計(jì)的另一種加密算法,密鑰長(zhǎng)度固定為 256 位,純軟件運(yùn)行性能要超過(guò) AES,曾經(jīng)在移動(dòng)客戶端上比較流行,但 ARMv8 之后也加入了 AES 硬件優(yōu)化,所以現(xiàn)在不再具有明顯的優(yōu)勢(shì),但仍然算得上是一個(gè)不錯(cuò)算法。
????????(2)加密分組模式
? ? ? ? ? 分組模式:對(duì)稱算法還有一個(gè)分組模式概念,它可以讓算法用固定長(zhǎng)度的密鑰加密任意長(zhǎng)度的明文,把小秘密(即密鑰)轉(zhuǎn)化為大秘密(即密文)。
????????最早有 ECB、CBC、CFB、OFB 等幾種分組模式,但都陸續(xù)被發(fā)現(xiàn)有安全漏洞,所以現(xiàn)在基本都不怎么用了。最新的分組模式被稱為 AEAD(Authenticated Encryption with Associated Data),在加密的同時(shí)增加了認(rèn)證的功能,常用的是 GCM、CCM 和Poly1305。
????????把上面這些組合起來(lái),就可以得到 TLS 密碼套件中定義的對(duì)稱加密算法。比如,AES128-GCM,意思是密鑰長(zhǎng)度為 128 位的 AES 算法,使用的分組模式是 GCM;ChaCha20-Poly1305 的意思是 ChaCha20 算法,使用的分組模式是 Poly1305。
????????(3)非對(duì)稱加密
????????密鑰交換:對(duì)稱加密看上去好像完美地實(shí)現(xiàn)了機(jī)密性,但其中有一個(gè)很大的問(wèn)題,如何把密鑰安全地傳遞給對(duì)方,術(shù)語(yǔ)叫密鑰交換。因?yàn)樵趯?duì)稱加密算法中只要持有密鑰就可以解密。如果密鑰在傳遞途中被黑客竊取,那他就可以隨意解密收發(fā)的數(shù)據(jù),通信過(guò)程也就沒(méi)有機(jī)密性可言了。所以,就出現(xiàn)了非對(duì)稱加密。
????????非對(duì)稱加密:也叫公鑰加密算法。它有兩個(gè)密鑰,一個(gè)叫“公鑰”(public key),一個(gè)叫“私鑰”(private key)。兩個(gè)密鑰是不同的,“不對(duì)稱”,公鑰可以公開(kāi)給任何人使用,而私鑰必須嚴(yán)格保密。非對(duì)稱加密可以解決“密鑰交換”的問(wèn)題。網(wǎng)站秘密保管私鑰,在網(wǎng)上任意分發(fā)公鑰,你想要登錄網(wǎng)站只要用公鑰加密就行了,密文只能由私鑰持有者才能解密。而黑客因?yàn)闆](méi)有私鑰,所以就無(wú)法破解密文。
? ????????單向性:公鑰加密后只能用私鑰解密,反過(guò)來(lái),私鑰加密后也只能用公鑰解密。
????????非對(duì)稱加密算法的設(shè)計(jì)要比對(duì)稱算法難得多,在 TLS 里只有很少的幾種,比如 DH、DSA、RSA、ECC 等。
????????RSA:可能是其中最著名的一個(gè),幾乎可以說(shuō)是非對(duì)稱加密的代名詞,它的安全性基于“整數(shù)分解”的數(shù)學(xué)難題,使用兩個(gè)超大素?cái)?shù)的乘積作為生成密鑰的材料,想要從公鑰推算出私鑰是非常困難的。10 年前 RSA 密鑰的推薦長(zhǎng)度是 1024,但隨著計(jì)算機(jī)運(yùn)算能力的提高,現(xiàn)在 1024 已經(jīng)不安全,普遍認(rèn)為至少要 2048 位。
????????ECC:(Elliptic Curve Cryptography)是非對(duì)稱加密里的“后起之秀”,它基于“橢圓曲線離散對(duì)數(shù)”的數(shù)學(xué)難題,使用特定的曲線方程和基點(diǎn)生成公鑰和私鑰,子算法 ECDHE 用于密鑰交換,ECDSA 用于數(shù)字簽名。目前比較常用的兩個(gè)曲線是 P-256(secp256r1,在 OpenSSL 稱為 prime256v1)和x25519。P-256 是 NIST(美國(guó)國(guó)家標(biāo)準(zhǔn)技術(shù)研究所)和 NSA(美國(guó)國(guó)家安全局)推薦使用的曲線,而 x25519 被認(rèn)為是最安全、最快速的曲線。ECC 名字里的“橢圓”經(jīng)常會(huì)引起誤解,其實(shí)它的曲線并不是橢圓形,只是因?yàn)榉匠毯茴愃朴?jì)算橢圓周長(zhǎng)的公式,實(shí)際的形狀更像拋物線,比如下面的圖就展示了兩個(gè)簡(jiǎn)單的橢圓曲線。
????????? RSA vs ECC:比起 RSA,ECC 在安全強(qiáng)度和性能上都有明顯的優(yōu)勢(shì)。160 位的 ECC 相當(dāng)于 1024 位的RSA,而 224 位的 ECC 則相當(dāng)于 2048 位的 RSA。因?yàn)槊荑€短,所以相應(yīng)的計(jì)算量、消耗的內(nèi)存和帶寬也就少,加密解密的性能就上去了,對(duì)于現(xiàn)在的移動(dòng)互聯(lián)網(wǎng)非常有吸引力。
????????(4)混合加密
? ? ? ? ? 性能問(wèn)題:雖然非對(duì)稱加密沒(méi)有“密鑰交換”的問(wèn)題,但因?yàn)樗鼈兌际腔趶?fù)雜的數(shù)學(xué)難題,運(yùn)算速度很慢,即使是 ECC 也要比 AES 差上好幾個(gè)數(shù)量級(jí)。如果僅用非對(duì)稱加密,雖然保證了安全,但通信速度有如烏龜、蝸牛,實(shí)用性就變成了零。
????????? 對(duì)比了 AES 和 RSA 這兩種算法的性能,下面列出了一次測(cè)試的結(jié)果:
aes_128_cbc enc/dec 1000 times : 0.97ms, 13.11MB/s
rsa_1024 enc/dec 1000 times : 138.59ms, 93.80KB/s
rsa_1024/aes ratio = 143.17
rsa_2048 enc/dec 1000 times : 840.35ms, 15.47KB/s
rsa_2048/aes ratio = 868.13
????????? 可以看到,RSA 的運(yùn)算速度是非常慢的,2048 位的加解密大約是 15KB/S(微秒或毫秒級(jí)),而 AES128 則是 13MB/S(納秒級(jí)),差了幾百倍。?????
????????混合加密:是不是能夠把對(duì)稱加密和非對(duì)稱加密結(jié)合起來(lái)呢,兩者互相取長(zhǎng)補(bǔ)短,即能高效地加密解密,又能安全地密鑰交換。這就是現(xiàn)在 TLS 里使用的混合加密方式,其實(shí)說(shuō)穿了也很簡(jiǎn)單:在通信剛開(kāi)始的時(shí)候使用非對(duì)稱算法,比如 RSA、ECDHE,首先解決密鑰交換的問(wèn)題。然后用隨機(jī)數(shù)產(chǎn)生對(duì)稱算法使用的“會(huì)話密鑰”(session key),再用公鑰加密。因?yàn)闀?huì)話密鑰很短,通常只有 16 字節(jié)或 32 字節(jié),所以慢一點(diǎn)也無(wú)所謂。對(duì)方拿到密文后用私鑰解密,取出會(huì)話密鑰。這樣,雙方就實(shí)現(xiàn)了對(duì)稱密鑰的安全交換,后續(xù)就不再使用非對(duì)稱加密,全都使用對(duì)稱加密。
???
????????3、數(shù)字簽名和證書(shū)
????????沒(méi)有完整性:黑客雖然拿不到會(huì)話密鑰,無(wú)法破解密文,但可以通過(guò)竊聽(tīng)收集到足夠多的密文,再嘗試著修改、重組后發(fā)給網(wǎng)站。因?yàn)闆](méi)有完整性保證,服務(wù)器只能“照單全收”,然后他就可以通過(guò)服務(wù)器的響應(yīng)獲取進(jìn)一步的線索,最終就會(huì)破解出明文。
????????偽造公鑰:黑客也可以偽造身份發(fā)布公鑰。如果你拿到了假的公鑰,混合加密就完全失效了。你以為自己是在和“某寶”通信,實(shí)際上網(wǎng)線的另一端卻是黑客,銀行卡號(hào)、密碼等敏感信息就在“安全”的通信過(guò)程中被竊取了。
????????? 所以,在機(jī)密性的基礎(chǔ)上還必須加上完整性、身份認(rèn)證等特性,才能實(shí)現(xiàn)真正的安全。
????????(1)摘要算法?
??????????摘要算法:實(shí)現(xiàn)完整性的手段主要是摘要算法(Digest Algorithm),也就是常說(shuō)的散列函數(shù)、哈希函數(shù)(Hash Function)。它能夠把任意長(zhǎng)度的數(shù)據(jù)轉(zhuǎn)換成固定長(zhǎng)度、而且獨(dú)一無(wú)二的“摘要”字符串,不能從摘要逆推出原文。
? ? ??
????????摘要算法實(shí)際上是把數(shù)據(jù)從一個(gè)“大空間”映射到了“小空間”,所以就存在“沖突”(collision,也叫碰撞)的可能性,可能會(huì)有兩份不同的原文對(duì)應(yīng)相同的摘要。好的摘要算法必須能夠“抵抗沖突”,讓這種可能性盡量地小。
????????? 因?yàn)檎惴▽?duì)輸入具有“單向性”和“雪崩效應(yīng)”,輸入的微小不同會(huì)導(dǎo)致輸出的劇烈變化,所以也被 TLS 用來(lái)生成偽隨機(jī)數(shù)(PRF,pseudo random function)。
????????? 你一定在日常工作中聽(tīng)過(guò)、或者用過(guò) MD5(Message-Digest 5)、SHA-1(Secure Hash Algorithm 1),它們就是最常用的兩個(gè)摘要算法,能夠生成 16 字節(jié)和 20 字節(jié)長(zhǎng)度的數(shù)字摘要。但這兩個(gè)算法的安全強(qiáng)度比較低,不夠安全,在 TLS 里已經(jīng)被禁止使用了。
????????? 目前 TLS 推薦使用的是 SHA-1 的后繼者:SHA-2。SHA-2 實(shí)際上是一系列摘要算法的統(tǒng)稱,總共有 6 種,常用的有 SHA224、SHA256、SHA384,分別能夠生成 28 字節(jié)、32 字節(jié)、48 字節(jié)的摘要。
????????(2)完整性
????????? 完整性:摘要算法保證了“數(shù)字摘要”和原文是完全等價(jià)的。所以,我們只要在原文后附上它的摘要,就能夠保證數(shù)據(jù)的完整性。如果黑客在中間哪怕改動(dòng)了一個(gè)標(biāo)點(diǎn)符號(hào),摘要也會(huì)完全不同,網(wǎng)站計(jì)算比對(duì)就會(huì)發(fā)現(xiàn)消息被竄改,是不可信的。
????????哈希消息認(rèn)證碼:不過(guò)摘要算法不具有機(jī)密性,如果明文傳輸,那么黑客可以修改消息后把摘要也一起改了,網(wǎng)站還是鑒別不出完整性。所以,真正的完整性必須要建立在機(jī)密性之上,在混合加密系統(tǒng)里用會(huì)話密鑰加密消息和摘要,這樣黑客無(wú)法得知明文,也就沒(méi)有辦法動(dòng)手腳了。這有個(gè)術(shù)語(yǔ),叫哈希消息認(rèn)證碼(HMAC)。
????????(3)數(shù)字簽名
? ????????端點(diǎn)安全:加密算法結(jié)合摘要算法,我們的通信過(guò)程可以說(shuō)是比較安全了。但這里還有漏洞,就是通信的兩個(gè)端點(diǎn)(endpoint)。黑客可以偽裝成網(wǎng)站來(lái)竊取信息。而反過(guò)來(lái),他也可以偽裝成你,向網(wǎng)站發(fā)送支付、轉(zhuǎn)賬等消息,網(wǎng)站沒(méi)有辦法確認(rèn)你的身份,錢(qián)可能就這么被偷走了。
????????? 數(shù)字簽名:私鑰+摘要算法=數(shù)字簽名,同時(shí)實(shí)現(xiàn)“身份認(rèn)證”和“不可否認(rèn)”。數(shù)字簽名的原理就是把公鑰私鑰用法反過(guò)來(lái),私鑰加密、公鑰解密。但又因?yàn)榉菍?duì)稱加密效率太低,所以私鑰只加密原文的摘要,這樣運(yùn)算量就小的多,而且得到的數(shù)字簽名也很小,方便保管和傳輸。簽名和公鑰一樣完全公開(kāi),任何人都可以獲取。但這個(gè)簽名只有用私鑰對(duì)應(yīng)的公鑰才能解開(kāi),拿到摘要后,再比對(duì)原文驗(yàn)證完整性,就可以像簽署文件一樣證明消息確實(shí)是你發(fā)的。
????????? 簽名和驗(yàn)簽:剛才的這兩個(gè)行為也有專用術(shù)語(yǔ),叫做“簽名”和“驗(yàn)簽”。只要你和網(wǎng)站互相交換公鑰,就可以用“簽名”和“驗(yàn)簽”來(lái)確認(rèn)消息的真實(shí)性,因?yàn)樗借€保密,黑客不能偽造簽名,就能夠保證通信雙方的身份。
????????(4)數(shù)字證書(shū)和CA
? ????????公鑰信任問(wèn)題:因?yàn)檎l(shuí)都可以發(fā)布公鑰,我們還缺少防止黑客偽造公鑰的手段。我們可以用別的私鑰來(lái)給公鑰簽名,顯然,這又會(huì)陷入“無(wú)窮遞歸”。要終結(jié)這個(gè)“死循環(huán)”,就必須引入“外力”,找一個(gè)公認(rèn)的可信第三方。
????????CA:(Certificate Authority,證書(shū)認(rèn)證機(jī)構(gòu)),這個(gè)第三方就是我們常說(shuō)的CA。它就像網(wǎng)絡(luò)世界里的公安局、教育部、公證中心,具有極高的可信度,由它來(lái)給各個(gè)公鑰簽名,用自身的信譽(yù)來(lái)保證公鑰無(wú)法偽造,是可信的。知名的 CA 全世界就那么幾家,比如 DigiCert、VeriSign、Entrust、Let’s Encrypt 等,它們簽發(fā)的證書(shū)分 DV、OV、EV 三種,區(qū)別在于可信程度。DV只是域名級(jí)別的可信,背后是誰(shuí)不知道。EV 是最高的,經(jīng)過(guò)了法律和審計(jì)的嚴(yán)格核查,可以證明網(wǎng)站擁有者的身份(在瀏覽器地址欄會(huì)顯示出公司的名字,例如Apple、GitHub 的網(wǎng)站)。
????????CA 對(duì)公鑰的簽名認(rèn)證格式:不是簡(jiǎn)單地把公鑰綁定在持有者身份上就完事了,還要包含序列號(hào)、用途、頒發(fā)者、有效時(shí)間等等,把這些打成一個(gè)包再簽名,完整地證明公鑰關(guān)聯(lián)的各種信息,形成數(shù)字證書(shū)。
????????自簽名證書(shū):小一點(diǎn)的 CA 可以讓大 CA 簽名認(rèn)證,但鏈條的最后,也就是Root CA,就只能自己證明自己了,這個(gè)就叫“自簽名證書(shū)”(Self-Signed Certificate)或者“根證書(shū)”(Root Certificate)。你必須相信,否則整個(gè)證書(shū)信任鏈就走不下去了。
? ??
????????有了這個(gè)證書(shū)體系,操作系統(tǒng)和瀏覽器都內(nèi)置了各大 CA 的根證書(shū),上網(wǎng)的時(shí)候只要服務(wù)器發(fā)過(guò)來(lái)它的證書(shū),就可以驗(yàn)證證書(shū)里的簽名,順著證書(shū)鏈(Certificate Chain)一層層地驗(yàn)證,直到找到根證書(shū),就能夠確定證書(shū)是可信的,從而里面的公鑰也是可信的。
?????????我們的實(shí)驗(yàn)環(huán)境里使用的證書(shū)是“野路子”的自簽名證書(shū)(在 Linux 上用 OpenSSL 命令行簽發(fā)),肯定是不會(huì)被瀏覽器所信任的,所以用 Chrome 訪問(wèn)時(shí)就會(huì)顯示成紅色,標(biāo)記為不安全。但你只要把它安裝進(jìn)系統(tǒng)的根證書(shū)存儲(chǔ)區(qū)里,讓它作為信任鏈的根,就不會(huì)再有危險(xiǎn)警告。
????????(5)證書(shū)體系的弱點(diǎn)
????????? 證書(shū)體系(PKI,Public Key Infrastructure)雖然是目前整個(gè)網(wǎng)絡(luò)世界的安全基礎(chǔ)設(shè)施,但絕對(duì)的安全是不存在的,它也有弱點(diǎn),還是關(guān)鍵的“信任”二字。
? ????????簽發(fā)了錯(cuò)誤的證書(shū):如果 CA 失誤或者被欺騙,簽發(fā)了錯(cuò)誤的證書(shū),雖然證書(shū)是真的,可它代表的網(wǎng)站卻是假的。針對(duì)這種情況,開(kāi)發(fā)出了 CRL(證書(shū)吊銷(xiāo)列表,Certificate revocation list)和 OCSP(在線證書(shū)狀態(tài)協(xié)議,Online Certificate Status Protocol),及時(shí)廢止有問(wèn)題的證書(shū)。
? ????????CA 被黑客攻陷:或者 CA 有惡意,因?yàn)樗?#xff08;即根證書(shū))是信任的源頭,整個(gè)信任鏈里的所有證書(shū)也就都不可信了。針對(duì)這種情況,就只能操作系統(tǒng)或者瀏覽器從根上“下狠手”了,撤銷(xiāo)對(duì) CA 的信任,列入“黑名單”,這樣它頒發(fā)的所有證書(shū)就都會(huì)被認(rèn)為是不安全的。
????????4、TLS1.2連接過(guò)程解析
????????(1)HTTPS建立連接
????????? 當(dāng)你在瀏覽器地址欄里鍵入“https”開(kāi)頭的 URI,再按下回車(chē),瀏覽器首先要從 URI 里提取出協(xié)議名和域名。因?yàn)閰f(xié)議名是“https”,所以瀏覽器就知道了端口號(hào)是默認(rèn)的 443,它再用 DNS 解析域名,得到目標(biāo)的 IP 地址,然后就可以使用三次握手與網(wǎng)站建立 TCP 連接了。
????????? 在 HTTP 協(xié)議里,建立連接后,瀏覽器會(huì)立即發(fā)送請(qǐng)求報(bào)文。但現(xiàn)在是 HTTPS 協(xié)議,它需要再用另外一個(gè)“握手”過(guò)程,在 TCP 上建立安全連接,之后才是收發(fā) HTTP 報(bào)文。這個(gè)“握手”過(guò)程與 TCP 有些類似,是 HTTPS 和 TLS 協(xié)議里最重要、最核心的部分。? ? ?
????????(2)TLS協(xié)議的組成
? ????????TLS 包含幾個(gè)子協(xié)議,你也可以理解為它是由幾個(gè)不同職責(zé)的模塊組成,比較常用的有記錄協(xié)議、警報(bào)協(xié)議、握手協(xié)議、變更密碼規(guī)范協(xié)議等。
- 記錄協(xié)議(Record Protocol)規(guī)定了 TLS 收發(fā)數(shù)據(jù)的基本單位:記錄(record)。它有點(diǎn)像是 TCP 里的 segment,所有的其他子協(xié)議都需要通過(guò)記錄協(xié)議發(fā)出。但多個(gè)記錄數(shù)據(jù)可以在一個(gè) TCP 包里一次性發(fā)出,也并不需要像 TCP 那樣返回 ACK。
- 警報(bào)協(xié)議(Alert Protocol)的職責(zé)是向?qū)Ψ桨l(fā)出警報(bào)信息,有點(diǎn)像是 HTTP 協(xié)議里的狀態(tài)碼。比如,protocol_version 就是不支持舊版本,bad_certificate 就是證書(shū)有問(wèn)題,收到警報(bào)后另一方可以選擇繼續(xù),也可以立即終止連接。
- 握手協(xié)議(Handshake Protocol)是 TLS 里最復(fù)雜的子協(xié)議,要比 TCP 的 SYN/ACK 復(fù)雜的多,瀏覽器和服務(wù)器會(huì)在握手過(guò)程中協(xié)商 TLS 版本號(hào)、隨機(jī)數(shù)、密碼套件等信息,然后交換證書(shū)和密鑰參數(shù),最終雙方協(xié)商得到會(huì)話密鑰,用于后續(xù)的混合加密系統(tǒng)。
- 變更密碼規(guī)范協(xié)議(Change Cipher Spec Protocol),它非常簡(jiǎn)單,就是一個(gè)“通知”,告訴對(duì)方,后續(xù)的數(shù)據(jù)都將使用加密保護(hù)。那么反過(guò)來(lái),在它之前,數(shù)據(jù)都是明文的。
????????? 下面的這張圖簡(jiǎn)要地描述了 TLS 的握手過(guò)程,其中每一個(gè)“框”都是一個(gè)記錄,多個(gè)記錄組合成一個(gè) TCP 包發(fā)送。所以,最多經(jīng)過(guò)兩次消息往返(4 個(gè)消息)就可以完成握手,然后就可以在安全的通信環(huán)境里發(fā)送 HTTP 報(bào)文,實(shí)現(xiàn) HTTPS 協(xié)議。
????????(3)ECDHE握手過(guò)程
????????? 剛才你看到的是握手過(guò)程的簡(jiǎn)要圖,我又畫(huà)了一個(gè)詳細(xì)圖,下面我就用這個(gè)圖來(lái)仔細(xì)剖析 TLS 的握手過(guò)程。
? ? ? ? ? ? ? ? ? ? ? ??
????????在 TCP 建立連接之后,瀏覽器會(huì)首先發(fā)一個(gè)“Client Hello”消息,也就是跟服務(wù)器“打招呼”。里面有客戶端的版本號(hào)、支持的密碼套件,還有一個(gè)隨機(jī)數(shù)(Client Random),用于后續(xù)生成會(huì)話密鑰。
Handshake Protocol: Client Hello
Version: TLS 1.2 (0x0303)
Random: 1cbf803321fd2623408dfe…
Cipher Suites (17 suites)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
????????? 服務(wù)器收到“Client Hello”后,會(huì)返回一個(gè)“Server Hello”消息。把版本號(hào)對(duì)一下,也給出一個(gè)隨機(jī)數(shù)(Server Random),然后從客戶端的列表里選一個(gè)作為本次通信使用的密碼套件,在這里它選擇了“TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384”。
Handshake Protocol: Server Hello
Version: TLS 1.2 (0x0303)
Random: 0e6320f21bae50842e96…
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
?????????這個(gè)的意思就是:“版本號(hào)對(duì)上了,可以加密,你的密碼套件挺多,我選一個(gè)最合適的吧,用橢圓曲線加 RSA、AES、SHA384。我也給你一個(gè)隨機(jī)數(shù),你也得留著?!?br /> ?????????然后,服務(wù)器為了證明自己的身份,就把證書(shū)也發(fā)給了客戶端(Server Certificate)。
?????????接下來(lái)是一個(gè)關(guān)鍵的操作,因?yàn)榉?wù)器選擇了 ECDHE 算法,所以它會(huì)在證書(shū)后發(fā)送“Server Key Exchange”消息,里面是橢圓曲線的公鑰(Server Params),用來(lái)實(shí)現(xiàn)密鑰交換算法,再加上自己的私鑰簽名認(rèn)證。
Handshake Protocol: Server Key Exchange
EC Diffie-Hellman Server Params
Curve Type: named_curve (0x03)
Named Curve: x25519 (0x001d)
Pubkey: 3b39deaf00217894e...
Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
Signature: 37141adac38ea4...
?????????這相當(dāng)于說(shuō):“剛才我選的密碼套件有點(diǎn)復(fù)雜,所以再給你個(gè)算法的參數(shù),和剛才的隨機(jī)數(shù)一樣有用,別丟了。為了防止別人冒充,我又蓋了個(gè)章?!?br /> ????????? 這樣第一個(gè)消息往返就結(jié)束了(兩個(gè) TCP 包),結(jié)果是客戶端和服務(wù)器通過(guò)明文共享了三個(gè)信息:Client Random、Server Random 和 Server Params。
????????客戶端這時(shí)也拿到了服務(wù)器的證書(shū),開(kāi)始走證書(shū)鏈逐級(jí)驗(yàn)證,確認(rèn)證書(shū)的真實(shí)性,再用證書(shū)公鑰驗(yàn)證簽名,就確認(rèn)了服務(wù)器的身份。
????????然后,客戶端按照密碼套件的要求,也生成一個(gè)橢圓曲線的公鑰(Client Params),用“Client Key Exchange”消息發(fā)給服務(wù)器。
?Handshake Protocol: Client Key Exchange
EC Diffie-Hellman Client Params
Pubkey: 8c674d0e08dc27b5eaa…
????????現(xiàn)在客戶端和服務(wù)器手里都拿到了密鑰交換算法的兩個(gè)參數(shù)(Client Params、Server Params),就用 ECDHE 算法算出了一個(gè)新的東西,叫“Pre-Master”,其實(shí)也是一個(gè)隨機(jī)數(shù)。算法可以保證即使黑客截獲了之前的參數(shù),也是絕對(duì)算不出這個(gè)隨機(jī)數(shù)的。
????????? 現(xiàn)在客戶端和服務(wù)器手里有了三個(gè)隨機(jī)數(shù):Client Random、Server Random 和 Pre-Master。用這三個(gè)作為原始材料,就可以生成用于加密會(huì)話的主密鑰,叫“Master Secret”。而黑客因?yàn)槟貌坏健癙re-Master”,所以也就得不到主密鑰。
????????這就必須說(shuō) TLS 的設(shè)計(jì)者考慮得非常周到了,他們不信任客戶端或服務(wù)器偽隨機(jī)數(shù)的可靠性,為了保證真正的“完全隨機(jī)”“不可預(yù)測(cè)”,把三個(gè)不可靠的隨機(jī)數(shù)混合起來(lái),那么“隨機(jī)”的程度就非常高了,足夠讓黑客難以猜測(cè)。
????????“Master Secret”究竟是怎么算出來(lái)的吧,貼一下 RFC 里的公式:
master_secret = PRF(pre_master_secret, "master secret",
ClientHello.random + ServerHello.random)
????????? 這里的“PRF”就是偽隨機(jī)數(shù)函數(shù),它基于密碼套件里的最后一個(gè)參數(shù),比如這次的SHA384,通過(guò)摘要算法來(lái)再一次強(qiáng)化“Master Secret”的隨機(jī)性。
????????? 主密鑰有 48 字節(jié),但它也不是最終用于通信的會(huì)話密鑰,還會(huì)再用 PRF 擴(kuò)展出更多的密鑰,比如客戶端發(fā)送用的會(huì)話密鑰(client_write_key)、服務(wù)器發(fā)送用的會(huì)話密鑰(server_write_key)等等,避免只用一個(gè)密鑰帶來(lái)的安全隱患。
????????有了主密鑰和派生的會(huì)話密鑰,握手就快結(jié)束了。客戶端發(fā)一個(gè)“Change Cipher Spec”,然后再發(fā)一個(gè)“Finished”消息,把之前所有發(fā)送的數(shù)據(jù)做個(gè)摘要,再加密一下,讓服務(wù)器做個(gè)驗(yàn)證。
?????????意思就是告訴服務(wù)器:“后面都改用對(duì)稱算法加密通信了啊,用的就是打招呼時(shí)說(shuō)的AES,加密對(duì)不對(duì)還得你測(cè)一下?!?br /> ????????? 服務(wù)器也是同樣的操作,發(fā)“Change Cipher Spec”和“Finished”消息,雙方都驗(yàn)證加密解密 OK,握手正式結(jié)束,后面就收發(fā)被加密的 HTTP 請(qǐng)求和響應(yīng)了。
????????
????????(4)RSA握手過(guò)程
????????? 主流的 TLS 握手過(guò)程與傳統(tǒng)的握手有兩點(diǎn)不同。
????????? 第一個(gè),使用 ECDHE 實(shí)現(xiàn)密鑰交換,而不是 RSA,所以會(huì)在服務(wù)器端發(fā)出“Server Key Exchange”消息。
????????第二個(gè),因?yàn)槭褂昧?ECDHE,客戶端可以不用等到服務(wù)器發(fā)回“Finished”確認(rèn)握手完畢,立即就發(fā)出 HTTP 報(bào)文,省去了一個(gè)消息往返的時(shí)間浪費(fèi)。這個(gè)叫“TLS False Start”,意思就是“搶跑”,和“TCP Fast Open”有點(diǎn)像,都是不等連接完全建立就提前發(fā)應(yīng)用數(shù)據(jù),提高傳輸?shù)男省?br />
????????大體的流程沒(méi)有變,只是“Pre-Master”不再需要用算法生成,而是客戶端直接生成隨機(jī)數(shù),然后用服務(wù)器的公鑰加密,通過(guò)“Client Key Exchange”消息發(fā)給服務(wù)器。服務(wù)器再用私鑰解密,這樣雙方也實(shí)現(xiàn)了共享三個(gè)隨機(jī)數(shù),就可以生成主密鑰。
????????(5)雙向認(rèn)證
? ????????不過(guò)上面說(shuō)的是“單向認(rèn)證”握手過(guò)程,只認(rèn)證了服務(wù)器的身份,而沒(méi)有認(rèn)證客戶端的身份。這是因?yàn)橥ǔ蜗蛘J(rèn)證通過(guò)后已經(jīng)建立了安全通信,用賬號(hào)、密碼等簡(jiǎn)單的手段就能夠確認(rèn)用戶的真實(shí)身份。
????????? 但為了防止賬號(hào)、密碼被盜,有的時(shí)候(比如網(wǎng)上銀行)還會(huì)使用 U 盾給用戶頒發(fā)客戶端證書(shū),實(shí)現(xiàn)“雙向認(rèn)證”,這樣會(huì)更加安全。
? ????????雙向認(rèn)證的流程也沒(méi)有太多變化,只是在“Server Hello Done”之后,“Client Key Exchange”之前,客戶端要發(fā)送“Client Certificate”消息,服務(wù)器收到后也把證書(shū)鏈走一遍,驗(yàn)證客戶端的身份。
????????
????????5、TLS1.3特性解析
????????TLS1.2 已經(jīng)是 10多年前(2008 年)的“老”協(xié)議了,雖然歷經(jīng)考驗(yàn),但畢竟“歲月不饒人”,在安全、性能等方面已經(jīng)跟不上如今的互聯(lián)網(wǎng)了。
????????于是經(jīng)過(guò)四年、近 30 個(gè)草案的反復(fù)打磨,TLS1.3 終于在2018 年“粉墨登場(chǎng)”,再次確立了信息安全領(lǐng)域的新標(biāo)準(zhǔn)。
????????TLS1.3 的三個(gè)主要改進(jìn)目標(biāo):兼容、安全與性能。? ??
????????(1)最大化兼容性
? ????????由于 1.1、1.2 等協(xié)議已經(jīng)出現(xiàn)了很多年,很多應(yīng)用軟件、中間代理(官方稱為“MiddleBox”)只認(rèn)老的記錄協(xié)議格式,更新改造很困難,甚至是不可行(設(shè)備僵化)。
????????? 為了保證這些被廣泛部署的“老設(shè)備”能夠繼續(xù)使用,避免新協(xié)議帶來(lái)的“沖擊”,TLS1.3 不得不做出妥協(xié),保持現(xiàn)有的記錄格式不變,通過(guò)“偽裝”來(lái)實(shí)現(xiàn)兼容,使得TLS1.3 看上去“像是”TLS1.2。
????????這要用到一個(gè)新的擴(kuò)展協(xié)議(Extension Protocol),它有點(diǎn)“補(bǔ)充條款”的意思,通過(guò)在記錄末尾添加一系列的“擴(kuò)展字段”來(lái)增加新的功能,老版本的 TLS 不認(rèn)識(shí)它可以直接忽略,這就實(shí)現(xiàn)了“后向兼容”。
????????在記錄頭的 Version 字段被兼容性“固定”的情況下,只要是 TLS1.3 協(xié)議,握手的“Hello”消息后面就必須有“supported_versions”擴(kuò)展,它標(biāo)記了 TLS 的版本號(hào),使用它就能區(qū)分新舊協(xié)議。
Handshake Protocol: Client Hello
Version: TLS 1.2 (0x0303)
Extension: supported_versions (len=11)
Supported Version: TLS 1.3 (0x0304)
Supported Version: TLS 1.2 (0x0303)
?????????TLS1.3 利用擴(kuò)展實(shí)現(xiàn)了許多重要的功能,比如“supported_groups”“key_share”“signature_algorithms”“server_name”等
????????
????????(2)強(qiáng)化安全
????????? TLS1.2 在十來(lái)年的應(yīng)用中獲得了許多寶貴的經(jīng)驗(yàn),陸續(xù)發(fā)現(xiàn)了很多的漏洞和加密算法的弱點(diǎn),所以 TLS1.3 就在協(xié)議里修補(bǔ)了這些不安全因素。
- 偽隨機(jī)數(shù)函數(shù)由 PRF 升級(jí)為 HKDF(HMAC-based Extract-and-Expand Key Derivation Function);
- 明確禁止在記錄協(xié)議里使用壓縮;
- 廢除了 RC4、DES 對(duì)稱加密算法;
- 廢除了 ECB、CBC 等傳統(tǒng)分組模式;
- 廢除了 MD5、SHA1、SHA-224 摘要算法;
- 廢除了 RSA、DH 密鑰交換算法和許多命名曲線。
?????????經(jīng)過(guò)這一番“減肥瘦身”之后,TLS1.3 里只保留了 AES、ChaCha20 對(duì)稱加密算法,分組模式只能用 AEAD 的 GCM、CCM 和 Poly1305,摘要算法只能用 SHA256、SHA384,密鑰交換算法只有 ECDHE 和 DHE,橢圓曲線也被“砍”到只剩 P-256 和 x25519 等 5種。
????????? 算法精簡(jiǎn)后帶來(lái)了一個(gè)意料之中的好處:原來(lái)眾多的算法、參數(shù)組合導(dǎo)致密碼套件非常復(fù)雜,難以選擇,而現(xiàn)在的 TLS1.3 里只有 5 個(gè)套件,無(wú)論是客戶端還是服務(wù)器都不會(huì)再犯“選擇困難癥”了。
????????這里還要特別說(shuō)一下廢除 RSA 和 DH 密鑰交換算法的原因。瀏覽器默認(rèn)會(huì)使用 ECDHE 而不是 RSA 做密鑰交換,這是因?yàn)樗痪哂小扒跋虬踩?#xff08;Forward Secrecy)。
????????? 假設(shè)有這么一個(gè)很有耐心的黑客,一直在長(zhǎng)期收集混合加密系統(tǒng)收發(fā)的所有報(bào)文。如果加密系統(tǒng)使用服務(wù)器證書(shū)里的 RSA 做密鑰交換,一旦私鑰泄露或被破解(使用社會(huì)工程學(xué)或者巨型計(jì)算機(jī)),那么黑客就能夠使用私鑰解密出之前所有報(bào)文的“Pre-Master”,再算出會(huì)話密鑰,破解所有密文。
????????而 ECDHE 算法在每次握手時(shí)都會(huì)生成一對(duì)臨時(shí)的公鑰和私鑰,每次通信的密鑰對(duì)都是不同的,也就是“一次一密”,即使黑客花大力氣破解了這一次的會(huì)話密鑰,也只是這次通信被攻擊,之前的歷史消息不會(huì)受到影響,仍然是安全的。
????????(3)提升性能
????????? HTTPS 建立連接時(shí)除了要做 TCP 握手,還要做 TLS 握手,在 1.2 中會(huì)多花兩個(gè)消息往返(2-RTT),可能導(dǎo)致幾十毫秒甚至上百毫秒的延遲,在移動(dòng)網(wǎng)絡(luò)中延遲還會(huì)更嚴(yán)重。
????????? 現(xiàn)在因?yàn)槊艽a套件大幅度簡(jiǎn)化,也就沒(méi)有必要再像以前那樣走復(fù)雜的協(xié)商流程了。TLS1.3壓縮了以前的“Hello”協(xié)商過(guò)程,刪除了“Key Exchange”消息,把握手時(shí)間減少到了“1-RTT”,效率提高了一倍。
????????其實(shí)具體的做法還是利用了擴(kuò)展??蛻舳嗽凇癈lient Hello”消息里直接用“supported_groups”帶上支持的曲線,比如 P-256、x25519,用“key_share”帶上曲線對(duì)應(yīng)的客戶端公鑰參數(shù),用“signature_algorithms”帶上簽名算法。
????????服務(wù)器收到后在這些擴(kuò)展里選定一個(gè)曲線和參數(shù),再用“key_share”擴(kuò)展返回服務(wù)器這邊的公鑰參數(shù),就實(shí)現(xiàn)了雙方的密鑰交換,后面的流程就和 1.2 基本一樣了。
????????? 除了標(biāo)準(zhǔn)的“1-RTT”握手,TLS1.3 還引入了“0-RTT”握手,用“pre_shared_key”和“early_data”擴(kuò)展,在 TCP 連接后立即就建立安全連接發(fā)送加密消息。
????????(4)握手分析
? ? ? ?
????????在 TCP 建立連接之后,瀏覽器首先還是發(fā)一個(gè)“Client Hello”。因?yàn)?1.3 的消息兼容 1.2,所以開(kāi)頭的版本號(hào)、支持的密碼套件和隨機(jī)數(shù)(Client Random)結(jié)構(gòu)都是一樣的(不過(guò)這時(shí)的隨機(jī)數(shù)是 32 個(gè)字節(jié))。? ? ? ? ? ? ? ? ? ?
????????? 注意“Client Hello”里的擴(kuò)展,“supported_versions”表示這是TLS1.3,“supported_groups”是支持的曲線,“key_share”是曲線對(duì)應(yīng)的參數(shù)。
????????? 服務(wù)器收到“Client Hello”同樣返回“Server Hello”消息,還是要給出一個(gè)**隨機(jī)數(shù)(Server Random)**和選定密碼套件。
?????????表面上看和 TLS1.2 是一樣的,重點(diǎn)是后面的擴(kuò)展?!皊upported_versions”里確認(rèn)使用的是 TLS1.3,然后在“key_share”擴(kuò)展帶上曲線和對(duì)應(yīng)的公鑰參數(shù)。
????????? 這時(shí)只交換了兩條消息,客戶端和服務(wù)器就拿到了四個(gè)共享信息:Client Random和Server Random、Client Params和Server Params,兩邊就可以各自用 ECDHE 算出“Pre-Master”,再用 HKDF 生成主密鑰“Master Secret”,效率比 TLS1.2 提高了一大截。
????????在算出主密鑰后,服務(wù)器立刻發(fā)出“Change Cipher Spec”消息,比 TLS1.2 提早進(jìn)入加密通信,后面的證書(shū)等就都是加密的了,減少了握手時(shí)的明文信息泄露。
????????這里 TLS1.3 還有一個(gè)安全強(qiáng)化措施,多了個(gè)“Certificate Verify”消息,用服務(wù)器的私鑰把前面的曲線、套件、參數(shù)等握手?jǐn)?shù)據(jù)加了簽名,作用和“Finished”消息差不多。但由于是私鑰簽名,所以強(qiáng)化了身份認(rèn)證和和防竄改。
????????這兩個(gè)“Hello”消息之后,客戶端驗(yàn)證服務(wù)器證書(shū),再發(fā)“Finished”消息,就正式完成了握手,開(kāi)始收發(fā) HTTP 報(bào)文。
????????6、HTTPS對(duì)連接慢的優(yōu)化
????????HTTPS 連接大致上可以劃分為兩個(gè)部分,第一個(gè)是建立連接時(shí)的非對(duì)稱加密握手,第二個(gè)是握手后的對(duì)稱加密報(bào)文傳輸。
????????由于目前流行的 AES、ChaCha20 性能都很好,還有硬件優(yōu)化,報(bào)文傳輸?shù)男阅軗p耗可以說(shuō)是非常地小,小到幾乎可以忽略不計(jì)了。所以,通常所說(shuō)的“HTTPS 連接慢”指的就是剛開(kāi)始建立連接的那段時(shí)間。
????????在 TCP 建連之后,正式數(shù)據(jù)傳輸之前,HTTPS 比 HTTP 增加了一個(gè) TLS 握手的步驟,這個(gè)步驟最長(zhǎng)可以花費(fèi)兩個(gè)消息往返,也就是 2-RTT。而且在握手消息的網(wǎng)絡(luò)耗時(shí)之外,還會(huì)有其他的一些“隱形”消耗,比如:
- 產(chǎn)生用于密鑰交換的臨時(shí)公私鑰對(duì)(ECDHE);
- 驗(yàn)證證書(shū)時(shí)訪問(wèn) CA 獲取 CRL 或者 OCSP;
- 非對(duì)稱加密解密處理“Pre-Master”。
????????? 在最差的情況下,也就是不做任何的優(yōu)化措施,HTTPS 建立連接可能會(huì)比 HTTP 慢上幾百毫秒甚至幾秒,這其中既有網(wǎng)絡(luò)耗時(shí),也有計(jì)算耗時(shí),就會(huì)讓人產(chǎn)生“打開(kāi)一個(gè) HTTPS 網(wǎng)站好慢啊”的感覺(jué)。
????????? 我畫(huà)了一張圖,把 TLS 握手過(guò)程中影響性能的部分都標(biāo)記了出來(lái),對(duì)照著它就可以“有的放矢”地來(lái)優(yōu)化 HTTPS。
????????(1)硬件優(yōu)化
????????? HTTPS 連接是計(jì)算密集型,而不是 I/O 密集型。所以,如果你花大價(jià)錢(qián)去買(mǎi)網(wǎng)卡、帶寬、SSD 存儲(chǔ)就是“南轅北轍”了,起不到優(yōu)化的效果。
?????????首先,你可以選擇更快的 CPU,最好還內(nèi)建 AES 優(yōu)化,這樣即可以加速握手,也可以加速傳輸。
????????其次,你可以選擇“SSL 加速卡”,加解密時(shí)調(diào)用它的 API,讓專門(mén)的硬件來(lái)做非對(duì)稱加解密,分擔(dān) CPU 的計(jì)算壓力。不過(guò)“SSL 加速卡”也有一些缺點(diǎn),比如升級(jí)慢、支持算法有限,不能靈活定制解決方案等。
????????所以,就出現(xiàn)了第三種硬件加速方式:“SSL 加速服務(wù)器”,用專門(mén)的服務(wù)器集群來(lái)徹底“卸載”TLS 握手時(shí)的加密解密計(jì)算,性能自然要比單純的“加速卡”要強(qiáng)大的多。
????????(2)軟件優(yōu)化
?????????不過(guò)硬件優(yōu)化方式中除了 CPU,其他的通??刹皇强亢?jiǎn)單花錢(qián)就能買(mǎi)到的,還要有一些開(kāi)發(fā)適配工作,有一定的實(shí)施難度。比如,“加速服務(wù)器”中關(guān)鍵的一點(diǎn)是通信必須是“異步”的,不能阻塞應(yīng)用服務(wù)器,否則加速就沒(méi)有意義了。
????????所以,軟件優(yōu)化的方式相對(duì)來(lái)說(shuō)更可行一些,性價(jià)比高,能夠“少花錢(qián),多辦事”。軟件方面的優(yōu)化還可以再分成兩部分:一個(gè)是軟件升級(jí),一個(gè)是協(xié)議優(yōu)化。
????????軟件升級(jí)實(shí)施起來(lái)比較簡(jiǎn)單,就是把現(xiàn)在正在使用的軟件盡量升級(jí)到最新版本,比如把Linux 內(nèi)核由 2.x 升級(jí)到 4.x,把 Nginx 由 1.6 升級(jí)到 1.16,把 OpenSSL 由 1.0.1 升級(jí)到1.1.0/1.1.1。由于這些軟件在更新版本的時(shí)候都會(huì)做性能優(yōu)化、修復(fù)錯(cuò)誤,只要運(yùn)維能夠主動(dòng)配合,這種軟件優(yōu)化是最容易做的,也是最容易達(dá)成優(yōu)化效果的。
????????但對(duì)于很多大中型公司來(lái)說(shuō),硬件升級(jí)或軟件升級(jí)都是個(gè)棘手的問(wèn)題,有成千上萬(wàn)臺(tái)各種型號(hào)的機(jī)器遍布各個(gè)機(jī)房,逐一升級(jí)不僅需要大量人手,而且有較高的風(fēng)險(xiǎn),可能會(huì)影響正常的線上服務(wù)。
????????所以,在軟硬件升級(jí)都不可行的情況下,我們最常用的優(yōu)化方式就是在現(xiàn)有的環(huán)境下挖掘協(xié)議自身的潛力。
????????(3)協(xié)議優(yōu)化
????????? 從剛才的 TLS 握手圖中你可以看到影響性能的一些環(huán)節(jié),協(xié)議優(yōu)化就要從這些方面著手,先來(lái)看看核心的密鑰交換過(guò)程。
????????? 如果有可能,應(yīng)當(dāng)盡量采用 TLS1.3,它大幅度簡(jiǎn)化了握手的過(guò)程,完全握手只要 1-RTT,而且更加安全。
????????如果暫時(shí)不能升級(jí)到 1.3,只能用 1.2,那么握手時(shí)使用的密鑰交換協(xié)議應(yīng)當(dāng)盡量選用橢圓曲線的 ECDHE 算法。它不僅運(yùn)算速度快,安全性高,還支持“False Start”,能夠把握手的消息往返由 2-RTT 減少到 1-RTT,達(dá)到與 TLS1.3 類似的效果。
????????另外,橢圓曲線也要選擇高性能的曲線,最好是 x25519,次優(yōu)選擇是 P-256。對(duì)稱加密算法方面,也可以選用“AES_128_GCM”,它能比“AES_256_GCM”略快一點(diǎn)點(diǎn)。
????????在 Nginx 里可以用“ssl_ciphers”“ssl_ecdh_curve”等指令配置服務(wù)器使用的密碼套件和橢圓曲線,把優(yōu)先使用的放在前面,例如:
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:EECDH+CHACHA20;?
ssl_ecdh_curve X25519:P-256;
????????(4)證書(shū)優(yōu)化
????????? 除了密鑰交換,握手過(guò)程中的證書(shū)驗(yàn)證也是一個(gè)比較耗時(shí)的操作,服務(wù)器需要把自己的證書(shū)鏈全發(fā)給客戶端,然后客戶端接收后再逐一驗(yàn)證。
????????? 這里就有兩個(gè)優(yōu)化點(diǎn),一個(gè)是證書(shū)傳輸,一個(gè)是證書(shū)驗(yàn)證。
????????服務(wù)器的證書(shū)可以選擇橢圓曲線(ECDSA)證書(shū)而不是 RSA 證書(shū),因?yàn)?224 位的 ECC 相當(dāng)于 2048 位的 RSA,所以橢圓曲線證書(shū)的“個(gè)頭”要比 RSA 小很多,即能夠節(jié)約帶寬也能減少客戶端的運(yùn)算量,可謂“一舉兩得”。
????????客戶端的證書(shū)驗(yàn)證其實(shí)是個(gè)很復(fù)雜的操作,除了要公鑰解密驗(yàn)證多個(gè)證書(shū)簽名外,因?yàn)樽C書(shū)還有可能會(huì)被撤銷(xiāo)失效,客戶端有時(shí)還會(huì)再去訪問(wèn) CA,下載 CRL 或者 OCSP 數(shù)據(jù),這又會(huì)產(chǎn)生 DNS 查詢、建立連接、收發(fā)數(shù)據(jù)等一系列網(wǎng)絡(luò)通信,增加好幾個(gè) RTT。
????????CRL(Certificate revocation list,證書(shū)吊銷(xiāo)列表)由 CA 定期發(fā)布,里面是所有被撤銷(xiāo)信任的證書(shū)序號(hào),查詢這個(gè)列表就可以知道證書(shū)是否有效。
????????但 CRL 因?yàn)槭恰岸ㄆ凇卑l(fā)布,就有“時(shí)間窗口”的安全隱患,而且隨著吊銷(xiāo)證書(shū)的增多,列表會(huì)越來(lái)越大,一個(gè) CRL 經(jīng)常會(huì)上 MB。想象一下,每次需要預(yù)先下載幾 M 的“無(wú)用數(shù)據(jù)”才能連接網(wǎng)站,實(shí)用性實(shí)在是太低了。
????????所以,現(xiàn)在 CRL 基本上不用了,取而代之的是 OCSP(在線證書(shū)狀態(tài)協(xié)議,Online Certificate Status Protocol),向 CA 發(fā)送查詢請(qǐng)求,讓 CA 返回證書(shū)的有效狀態(tài)。
????????但 OCSP 也要多出一次網(wǎng)絡(luò)請(qǐng)求的消耗,而且還依賴于 CA 服務(wù)器,如果 CA 服務(wù)器很忙,那響應(yīng)延遲也是等不起的。
????????于是又出來(lái)了一個(gè)“補(bǔ)丁”,叫“OCSP Stapling”(OCSP 裝訂),它可以讓服務(wù)器預(yù)先訪問(wèn) CA 獲取 OCSP 響應(yīng),然后在握手時(shí)隨著證書(shū)一起發(fā)給客戶端,免去了客戶端連接 CA服務(wù)器查詢的時(shí)間。
????????
????????(5)會(huì)話復(fù)用
????????? 回想一下 HTTPS 建立連接的過(guò)程:先是 TCP 三次握手,然后是 TLS 一次握手。這后一次握手的重點(diǎn)是算出主密鑰“Master Secret”,而主密鑰每次連接都要重新計(jì)算太浪費(fèi)了,如果能夠把算出來(lái)的主密鑰緩存一下“重用”,就可以免去了握手和計(jì)算的成本,這種做法就叫“會(huì)話復(fù)用”(TLS session resumption),和 HTTP Cache 一樣,也是提高 HTTPS 性能的“大殺器”,被瀏覽器和服務(wù)器廣泛應(yīng)用。
????????? 會(huì)話復(fù)用分兩種,第一種叫“Session ID”,就是客戶端和服務(wù)器首次連接后各自保存一個(gè)會(huì)話的 ID 號(hào),內(nèi)存里存儲(chǔ)主密鑰和其他相關(guān)的信息。當(dāng)客戶端再次連接時(shí)發(fā)一個(gè) ID 過(guò)來(lái),服務(wù)器就在內(nèi)存里找,找到就直接用主密鑰恢復(fù)會(huì)話狀態(tài),跳過(guò)證書(shū)驗(yàn)證和密鑰交換,只用一個(gè)消息往返就可以建立安全通信。
????????? 服務(wù)器在“ServerHello”消息后直接發(fā)送了“Change Cipher Spec”和“Finished”消息,復(fù)用會(huì)話完成了握手。
? ? ??
????????(6)會(huì)話票證
????????? “Session ID”是最早出現(xiàn)的會(huì)話復(fù)用技術(shù),也是應(yīng)用最廣的,但它也有缺點(diǎn),服務(wù)器必須保存每一個(gè)客戶端的會(huì)話數(shù)據(jù),對(duì)于擁有百萬(wàn)、千萬(wàn)級(jí)別用戶的網(wǎng)站來(lái)說(shuō)存儲(chǔ)量就成了大問(wèn)題,加重了服務(wù)器的負(fù)擔(dān)。
?????????于是,又出現(xiàn)了第二種“Session Ticket”方案。它有點(diǎn)類似 HTTP 的 Cookie,存儲(chǔ)的責(zé)任由服務(wù)器轉(zhuǎn)移到了客戶端,服務(wù)器加密會(huì)話信息,用“New Session Ticket”消息發(fā)給客戶端,讓客戶端保存。
????????重連的時(shí)候,客戶端使用擴(kuò)展“session_ticket”發(fā)送“Ticket”而不是“Session ID”,服務(wù)器解密后驗(yàn)證有效期,就可以恢復(fù)會(huì)話,開(kāi)始加密通信。
????????不過(guò)“Session Ticket”方案需要使用一個(gè)固定的密鑰文件(ticket_key)來(lái)加密 Ticket,為了防止密鑰被破解,保證“前向安全”,密鑰文件需要定期輪換,比如設(shè)置為一小時(shí)或者一天。
????????
????????(7)預(yù)共享密鑰
????????? “False Start”“Session ID”“Session Ticket”等方式只能實(shí)現(xiàn) 1-RTT,而 TLS1.3 更進(jìn)一步實(shí)現(xiàn)了“0-RTT”,原理和“Session Ticket”差不多,但在發(fā)送 Ticket 的同時(shí)會(huì)帶上應(yīng)用數(shù)據(jù)(Early Data),免去了 1.2 里的服務(wù)器確認(rèn)步驟,這種方式叫“Pre shared Key”,簡(jiǎn)稱為“PSK”。
? ? ?
????????但“PSK”也不是完美的,它為了追求效率而犧牲了一點(diǎn)安全性,容易受到“重放攻擊”(Replay attack)的威脅。黑客可以截獲“PSK”的數(shù)據(jù),像復(fù)讀機(jī)那樣反復(fù)向服務(wù)器發(fā)送。
????????? 解決的辦法是只允許安全的 GET/HEAD 方法,在消息里加入時(shí)間戳、“nonce”驗(yàn)證,或者“一次性票證”限制重放。
????????7、遷移到HTTPS的必要性
????????(1)遷移的必要性
????????? 如果你做移動(dòng)應(yīng)用開(kāi)發(fā)的話,那么就一定知道,Apple、Android、某信等開(kāi)發(fā)平臺(tái)在2017 年就相繼發(fā)出通知,要求所有的應(yīng)用必須使用 HTTPS 連接,禁止不安全的 HTTP。
????????? 在臺(tái)式機(jī)上,主流的瀏覽器 Chrome、Firefox 等也早就開(kāi)始“強(qiáng)推”HTTPS,把 HTTP 站點(diǎn)打上“不安全”的標(biāo)簽,給用戶以“心理壓力”。
????????Google 等搜索巨頭還利用自身的“話語(yǔ)權(quán)”優(yōu)勢(shì),降低 HTTP 站點(diǎn)的排名,而給 HTTPS更大的權(quán)重,力圖讓網(wǎng)民只訪問(wèn)到 HTTPS 網(wǎng)站。
????????這些手段都逐漸“擠壓”了純明文 HTTP 的生存空間,“遷移到 HTTPS”已經(jīng)不是“要不要做”的問(wèn)題,而是“要怎么做”的問(wèn)題了。HTTPS 的大潮無(wú)法阻擋,如果還是死守著HTTP,那么無(wú)疑會(huì)被沖刷到互聯(lián)網(wǎng)的角落里。
????????? 目前國(guó)內(nèi)外的許多知名大站都已經(jīng)實(shí)現(xiàn)了“全站 HTTPS”。
????????
????????(2)遷移的顧慮
????????慢:認(rèn)為 HTTPS 會(huì)增加服務(wù)器的成本,增加客戶端的時(shí)延,影響用戶體驗(yàn)。其實(shí)現(xiàn)在服務(wù)器和客戶端的運(yùn)算能力都已經(jīng)有了很大的提升,性能方面完全沒(méi)有擔(dān)心的必要,而且還可以應(yīng)用很多的優(yōu)化解決方案。根據(jù) Google 等公司的評(píng)估,在經(jīng)過(guò)適當(dāng)優(yōu)化之后,HTTPS 的額外 CPU 成本小于 1%,額外的網(wǎng)絡(luò)成本小于 2%,可以說(shuō)是與無(wú)加密的 HTTP 相差無(wú)幾。
????????貴:主要是指證書(shū)申請(qǐng)和維護(hù)的成本太高,網(wǎng)站難以承擔(dān)。為了推廣 HTTPS,很多云服務(wù)廠商都提供了一鍵申請(qǐng)、價(jià)格低廉的證書(shū),而且還出現(xiàn)了專門(mén)頒發(fā)免費(fèi)證書(shū)的 CA,其中最著名的就是“Let’s Encrypt”。
????????難:是指 HTTPS 涉及的知識(shí)點(diǎn)太多、太復(fù)雜,有一定的技術(shù)門(mén)檻,不能很快上手。這第三個(gè)顧慮比較現(xiàn)實(shí),HTTPS 背后關(guān)聯(lián)到了密碼學(xué)、TLS、PKI 等許多領(lǐng)域,不是短短幾周、幾個(gè)月就能夠精通的。但實(shí)施 HTTPS 也并不需要把這些完全掌握,只要抓住少數(shù)幾個(gè)要點(diǎn)就好,下面我就來(lái)幫你逐個(gè)解決一些關(guān)鍵的“難點(diǎn)”。
????????(3)申請(qǐng)證書(shū)
????????? 大型網(wǎng)站出于信譽(yù)、公司形象的考慮,通常會(huì)選擇向傳統(tǒng)的 CA 申請(qǐng)證書(shū),例如DigiCert、GlobalSign,而中小型網(wǎng)站完全可以選擇使用“Let’s Encrypt”這樣的免費(fèi)證書(shū),效果也完全不輸于那些收費(fèi)的證書(shū)。
????????? “Let’s Encrypt”一直在推動(dòng)證書(shū)的自動(dòng)化部署,為此還實(shí)現(xiàn)了專門(mén)的 ACME 協(xié)議(RFC8555)。有很多的客戶端軟件可以完成申請(qǐng)、驗(yàn)證、下載、更新的“一條龍”操作,比如 Certbot、acme.sh 等等,都可以在“Let’s Encrypt”網(wǎng)站上找到,用法很簡(jiǎn)單,相關(guān)的文檔也很詳細(xì),幾分鐘就能完成申請(qǐng)。
????????不過(guò)我必須提醒你幾個(gè)注意事項(xiàng)。
????????第一,申請(qǐng)證書(shū)時(shí)應(yīng)當(dāng)同時(shí)申請(qǐng) RSA 和 ECDSA 兩種證書(shū),在 Nginx 里配置成雙證書(shū)驗(yàn)證,這樣服務(wù)器可以自動(dòng)選擇快速的橢圓曲線證書(shū),同時(shí)也兼容只支持 RSA 的客戶端。
????????第二,如果申請(qǐng) RSA 證書(shū),私鑰至少要 2048 位,摘要算法應(yīng)該選用 SHA-2,例如SHA256、SHA384 等。
????????第三,出于安全的考慮,“Let’s Encrypt”證書(shū)的有效期很短,只有 90 天,時(shí)間一到就會(huì)過(guò)期失效,所以必須要定期更新。你可以在 crontab 里加個(gè)每周或每月任務(wù),發(fā)送更新請(qǐng)求,不過(guò)很多 ACME 客戶端會(huì)自動(dòng)添加這樣的定期任務(wù),完全不用你操心。
????????(4)配置HTTPS
????????? 這在 Nginx 上非常簡(jiǎn)單,只要在“l(fā)isten”指令后面加上參數(shù)“ssl”,再配上剛才的證書(shū)文件就可以實(shí)現(xiàn)最基本的 HTTPS。
listen 443 ssl;
ssl_certificate xxx_rsa.crt; #rsa2048 cert
ssl_certificate_key xxx_rsa.key; #rsa2048 private key
ssl_certificate xxx_ecc.crt; #ecdsa cert
ssl_certificate_key xxx_ecc.key; #ecdsa private ke
????????為了提高 HTTPS 的安全系數(shù)和性能,你還可以強(qiáng)制 Nginx 只支持 TLS1.2 以上的協(xié)議,打開(kāi)“Session Ticket”會(huì)話復(fù)用:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 5m;
ssl_session_tickets on;
ssl_session_ticket_key ticket.key;
????????密碼套件的選擇方面,我給你的建議是以服務(wù)器的套件優(yōu)先。這樣可以避免惡意客戶端故意選擇較弱的套件、降低安全等級(jí),然后密碼套件向 TLS1.3“看齊”,只使用 ECDHE、AES和 ChaCha20,支持“False Start”。
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128
?????????如果你的服務(wù)器上使用了 OpenSSL 的分支 BorringSSL,那么還可以使用一個(gè)特殊的“等價(jià)密碼組”(Equal preference cipher groups)特性,它可以讓服務(wù)器配置一組“等價(jià)”的密碼套件,在這些套件里允許客戶端優(yōu)先選擇,比如這么配置:?
ssl_ciphers?
[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305];? ? ? ?
????????? 如果客戶端硬件沒(méi)有 AES 優(yōu)化,服務(wù)器就會(huì)順著客戶端的意思,優(yōu)先選擇與 AES“等價(jià)”的 ChaCha20 算法,讓客戶端能夠快一點(diǎn)。
????????? 全部配置完成后,你可以訪問(wèn)“SSLLabs”網(wǎng)站,測(cè)試網(wǎng)站的安全程度,它會(huì)模擬多種客戶端發(fā)起測(cè)試,打出一個(gè)綜合的評(píng)分。
????????(5)服務(wù)器名稱指示
????????? 配置 HTTPS 服務(wù)時(shí)還有一個(gè)“虛擬主機(jī)”的問(wèn)題需要解決。在 HTTP 協(xié)議里,多個(gè)域名可以同時(shí)在一個(gè) IP 地址上運(yùn)行,這就是“虛擬主機(jī)”,Web服務(wù)器會(huì)使用請(qǐng)求頭里的 Host 字段來(lái)選擇。
????????? 但在 HTTPS 里,因?yàn)檎?qǐng)求頭只有在 TLS 握手之后才能發(fā)送,在握手時(shí)就必須選擇“虛擬主機(jī)”對(duì)應(yīng)的證書(shū),TLS 無(wú)法得知域名的信息,就只能用 IP 地址來(lái)區(qū)分。所以,最早的時(shí)候每個(gè) HTTPS 域名必須使用獨(dú)立的 IP 地址,非常不方便。
????????那么怎么解決這個(gè)問(wèn)題呢?這還是得用到 TLS 的“擴(kuò)展”,給協(xié)議加個(gè)SNI(Server Name Indication)的“補(bǔ)充條款”。它的作用和 Host 字段差不多,客戶端會(huì)在“Client Hello”時(shí)帶上域名信息,這樣服務(wù)器就可以根據(jù)名字而不是 IP 地址來(lái)選擇證書(shū)。
Extension: server_name (len=19)
Server Name Indication extension
Server Name Type: host_name (0)
Server Name: www.chrono.com
?????????Nginx 很早就基于 SNI 特性支持了 HTTPS 的虛擬主機(jī),在 OpenResty 里可還以編寫(xiě)Lua 腳本,利用 Redis、MySQL 等數(shù)據(jù)庫(kù)更靈活快速地加載證書(shū)。
????????(6)重定向跳轉(zhuǎn)
? ? ? ? ? 現(xiàn)在有了 HTTPS 服務(wù),但原來(lái)的 HTTP 站點(diǎn)也不能馬上棄用,還是會(huì)有很多網(wǎng)民習(xí)慣在地址欄里直接敲域名(或者是舊的書(shū)簽、超鏈接),默認(rèn)使用 HTTP 協(xié)議訪問(wèn)。
????????? 所以,我們就需要用到“重定向跳轉(zhuǎn)”技術(shù)了,把不安全的 HTTP 網(wǎng)址用 301或 302“重定向”到新的 HTTPS 網(wǎng)站,在 Nginx 里也很容易做到,使“return”或“rewrite”都可以。
return 301 https://$host$request_uri; # 永久重定向
rewrite ^ https://$host$request_uri permanent; # 永久重定向
????????? 但這種方式有兩個(gè)問(wèn)題。一個(gè)是重定向增加了網(wǎng)絡(luò)成本,多出了一次請(qǐng)求;另一個(gè)是存在安全隱患,重定向的響應(yīng)可能會(huì)被“中間人”竄改,實(shí)現(xiàn)“會(huì)話劫持”,跳轉(zhuǎn)到惡意網(wǎng)站。
????????? 不過(guò)有一種叫“HSTS”(HTTP 嚴(yán)格傳輸安全,HTTP Strict Transport Security)的技術(shù)可以消除這種安全隱患。HTTPS 服務(wù)器需要在發(fā)出的響應(yīng)頭里添加一個(gè)“Strict-Transport-Security”的字段,再設(shè)定一個(gè)有效期,例如:
Strict-Transport-Security: max-age=15768000; includeSubDomains
????????這相當(dāng)于告訴瀏覽器:我這個(gè)網(wǎng)站必須嚴(yán)格使用 HTTPS 協(xié)議,在半年之內(nèi)(182.5 天)都不允許用 HTTP,你以后就自己做轉(zhuǎn)換吧,不要再來(lái)麻煩我了。
????????? 有了“HSTS”的指示,以后瀏覽器再訪問(wèn)同樣的域名的時(shí)候就會(huì)自動(dòng)把 URI 里的“http”改成“https”,直接訪問(wèn)安全的 HTTPS 網(wǎng)站。這樣“中間人”就失去了攻擊的機(jī)會(huì),而且對(duì)于客戶端來(lái)說(shuō)也免去了一次跳轉(zhuǎn),加快了連接速度。
五、未來(lái)
????????1、HTTP/2特性概覽
?? ??? ?HTTP 有兩個(gè)主要的缺點(diǎn):安全不足和性能不高。通過(guò)引入 SSL/TLS 在安全上達(dá)到了“極致”,但在性能提升方面卻是乏善可陳,只優(yōu)化了握手加密的環(huán)節(jié),對(duì)于整體的數(shù)據(jù)傳輸沒(méi)有提出更好的改進(jìn)方案,還只能依賴于“長(zhǎng)連接”這種“落后”的技術(shù)
????????所以,在 HTTPS 逐漸成熟之后,HTTP 就向著性能方面開(kāi)始“發(fā)力”,走出了另一條進(jìn)化的道路。
????????在HTTP 歷史中你也看到了,Google 率先發(fā)明了SPDY 協(xié)議,并應(yīng)用于自家的瀏覽器 Chrome,打響了 HTTP 性能優(yōu)化的“第一槍”。隨后互聯(lián)網(wǎng)標(biāo)準(zhǔn)化組織 IETF 以 SPDY 為基礎(chǔ),綜合其他多方的意見(jiàn),終于推出了 HTTP/1的繼任者,也就是今天的主角“HTTP/2”,在性能方面有了一個(gè)大的飛躍。
????????(1)為什么不是HTTP/2.0
????????? 他們認(rèn)為以前的“1.0”“1.1”造成了很多的混亂和誤解,讓人在實(shí)際的使用中難以區(qū)分差異,所以就決定 HTTP 協(xié)議不再使用小版本號(hào)(minor version),只使用大版本號(hào)(major version),從今往后 HTTP 協(xié)議不會(huì)出現(xiàn) HTTP/2.0、2.1,只會(huì)有“HTTP/2”“HTTP/3”……
????????(2)兼容HTTP/1
????????? 協(xié)議的修改必須小心謹(jǐn)慎,兼容性是首要考慮的目標(biāo),否則就會(huì)破壞互聯(lián)網(wǎng)上無(wú)數(shù)現(xiàn)有的資產(chǎn),這方面TLS 已經(jīng)有了先例(為了兼容 TLS1.2 不得不進(jìn)行“偽裝”)。
????????? 因?yàn)楸仨氁3止δ苌系募嫒?#xff0c;所以 HTTP/2 把 HTTP 分解成了“語(yǔ)義”和“語(yǔ)法”兩個(gè)部分,“語(yǔ)義”層不做改動(dòng),與 HTTP/1 完全一致(即 RFC7231)。比如請(qǐng)求方法、URI、狀態(tài)碼、頭字段等概念都保留不變,這樣就消除了再學(xué)習(xí)的成本,基于 HTTP 的上層應(yīng)用也不需要做任何修改,可以無(wú)縫轉(zhuǎn)換到 HTTP/2。
????????特別要說(shuō)的是,與 HTTPS 不同,HTTP/2 沒(méi)有在 URI 里引入新的協(xié)議名,仍然用“http”表示明文協(xié)議,用“https”表示加密協(xié)議。這是一個(gè)非常了不起的決定,可以讓瀏覽器或者服務(wù)器去自動(dòng)升級(jí)或降級(jí)協(xié)議,免去了選擇的麻煩,讓用戶在上網(wǎng)的時(shí)候都意識(shí)不到協(xié)議的切換,實(shí)現(xiàn)平滑過(guò)渡。
????????在“語(yǔ)義”保持穩(wěn)定之后,HTTP/2 在“語(yǔ)法”層做了“天翻地覆”的改造,完全變更了HTTP 報(bào)文的傳輸格式。
????????(3)頭部壓縮
????????? HTTP/1 里可以用頭字段“Content-Encoding”指定Body 的編碼方式,比如用 gzip 壓縮來(lái)節(jié)約帶寬,但報(bào)文的另一個(gè)組成部分——Header卻被無(wú)視了,沒(méi)有針對(duì)它的優(yōu)化手段。
????????由于報(bào)文 Header 一般會(huì)攜帶“User Agent”“Cookie”“Accept”“Server”等許多固定的頭字段,多達(dá)幾百字節(jié)甚至上千字節(jié),但 Body 卻經(jīng)常只有幾十字節(jié)(比如 GET 請(qǐng)求、204/301/304 響應(yīng)),成了不折不扣的“大頭兒子”。更要命的是,成千上萬(wàn)的請(qǐng)求響應(yīng)報(bào)文里有很多字段值都是重復(fù)的,非常浪費(fèi),“長(zhǎng)尾效應(yīng)”導(dǎo)致大量帶寬消耗在了這些冗余度極高的數(shù)據(jù)上。
????????不過(guò) HTTP/2 并沒(méi)有使用傳統(tǒng)的壓縮算法,而是開(kāi)發(fā)了專門(mén)的“HPACK”算法,在客戶端和服務(wù)器兩端建立“字典”,用索引號(hào)表示重復(fù)的字符串,還釆用哈夫曼編碼來(lái)壓縮整數(shù)和字符串,可以達(dá)到 50%~90% 的高壓縮率。
????????(4)二進(jìn)制格式
????????? 你可能已經(jīng)很習(xí)慣于 HTTP/1 里純文本形式的報(bào)文了,它的優(yōu)點(diǎn)是“一目了然”,用最簡(jiǎn)單的工具就可以開(kāi)發(fā)調(diào)試,非常方便。
????????? 但 HTTP/2 在這方面沒(méi)有“妥協(xié)”,決定改變延續(xù)了十多年的現(xiàn)狀,不再使用肉眼可見(jiàn)的ASCII 碼,而是向下層的 TCP/IP 協(xié)議“靠攏”,全面采用二進(jìn)制格式。
????????這樣雖然對(duì)人不友好,但卻大大方便了計(jì)算機(jī)的解析。原來(lái)使用純文本的時(shí)候容易出現(xiàn)多義性,比如大小寫(xiě)、空白字符、回車(chē)換行、多字少字等等,程序在處理時(shí)必須用復(fù)雜的狀態(tài)機(jī),效率低,還麻煩。
????????而二進(jìn)制里只有“0”和“1”,可以嚴(yán)格規(guī)定字段大小、順序、標(biāo)志位等格式,“對(duì)就是對(duì),錯(cuò)就是錯(cuò)”,解析起來(lái)沒(méi)有歧義,實(shí)現(xiàn)簡(jiǎn)單,而且體積小、速度快,做到“內(nèi)部提效”。
????????HTTP/2以二進(jìn)制格式為基礎(chǔ),把 TCP 協(xié)議的部分特性挪到了應(yīng)用層,把原來(lái)的“Header+Body”的消息“打散”為數(shù)個(gè)小片的二進(jìn)制“幀”(Frame),用“HEADERS”幀存放頭數(shù)據(jù)、“DATA”幀存放實(shí)體數(shù)據(jù)。
????????HTTP/2 數(shù)據(jù)分幀后“Header+Body”的報(bào)文結(jié)構(gòu)就完全消失了,協(xié)議看到的只是一個(gè)個(gè)的“碎片”。
????????(5)虛擬的流
????????? 消息的“碎片”到達(dá)目的地后應(yīng)該怎么組裝起來(lái),HTTP/2 為此定義了一個(gè)“流”(Stream)的概念,它是二進(jìn)制幀的雙向傳輸序列,同一個(gè)消息往返的幀會(huì)分配一個(gè)唯一的流 ID。你可以把它想象成是一個(gè)虛擬的“數(shù)據(jù)流”,在里面流動(dòng)的是一串有先后順序的數(shù)據(jù)幀,這些數(shù)據(jù)幀按照次序組裝起來(lái)就是 HTTP/1 里的請(qǐng)求報(bào)文和響應(yīng)報(bào)文。
????????? 因?yàn)椤傲鳌笔翘摂M的,實(shí)際上并不存在,所以 HTTP/2 就可以在一個(gè) TCP 連接上用“流”同時(shí)發(fā)送多個(gè)“碎片化”的消息,這就是常說(shuō)的“多路復(fù)用”( Multiplexing)——多個(gè)往返通信都復(fù)用一個(gè)連接來(lái)處理。
????????在“流”的層面上看,消息是一些有序的“幀”序列,而在“連接”的層面上看,消息卻是亂序收發(fā)的“幀”。多個(gè)請(qǐng)求 / 響應(yīng)之間沒(méi)有了順序關(guān)系,不需要排隊(duì)等待,也就不會(huì)再出現(xiàn)“隊(duì)頭阻塞”問(wèn)題,降低了延遲,大幅度提高了連接的利用率。
????????? 為了更好地利用連接,加大吞吐量,HTTP/2 還添加了一些控制幀來(lái)管理虛擬的“流”,實(shí)現(xiàn)了優(yōu)先級(jí)和流量控制,這些特性也和 TCP 協(xié)議非常相似。
????????? HTTP/2 還在一定程度上改變了傳統(tǒng)的“請(qǐng)求 - 應(yīng)答”工作模式,服務(wù)器不再是完全被動(dòng)地響應(yīng)請(qǐng)求,也可以新建“流”主動(dòng)向客戶端發(fā)送消息。比如,在瀏覽器剛請(qǐng)求 HTML 的時(shí)候就提前把可能會(huì)用到的 JS、CSS 文件發(fā)給客戶端,減少等待的延遲,這被稱為“服務(wù)器推送”(Server Push,也叫 Cache Push)。
????????(6)強(qiáng)化安全
????????? 出于兼容的考慮,HTTP/2 延續(xù)了 HTTP/1 的“明文”特點(diǎn),可以像以前一樣使用明文傳輸數(shù)據(jù),不強(qiáng)制使用加密通信,不過(guò)格式還是二進(jìn)制,只是不需要解密。
????????? 但由于 HTTPS 已經(jīng)是大勢(shì)所趨,而且主流的瀏覽器 Chrome、Firefox 等都公開(kāi)宣布只支持加密的 HTTP/2,所以“事實(shí)上”的 HTTP/2 是加密的。也就是說(shuō),互聯(lián)網(wǎng)上通常所能見(jiàn)到的 HTTP/2 都是使用“https”協(xié)議名,跑在 TLS 上面。
????????為了區(qū)分“加密”和“明文”這兩個(gè)不同的版本,HTTP/2 協(xié)議定義了兩個(gè)字符串標(biāo)識(shí)符:“h2”表示加密的 HTTP/2,“h2c”表示明文的 HTTP/2,多出的那個(gè)字母“c”的意思是“clear text”。
????????在 HTTP/2 標(biāo)準(zhǔn)制定的時(shí)候(2015 年)已經(jīng)發(fā)現(xiàn)了很多 SSL/TLS 的弱點(diǎn),而新的 TLS1.3還未發(fā)布,所以加密版本的 HTTP/2 在安全方面做了強(qiáng)化,要求下層的通信協(xié)議必須是TLS1.2 以上,還要支持前向安全和 SNI,并且把幾百個(gè)弱密碼套件列入了“黑名單”,比如 DES、RC4、CBC、SHA-1 都不能在 HTTP/2 里使用,相當(dāng)于底層用的是“TLS1.25”。
????????(7)協(xié)議棧
????????? 下面的這張圖對(duì)比了 HTTP/1、HTTPS 和 HTTP/2 的協(xié)議棧,你可以清晰地看到,HTTP/2是建立在“HPack”“Stream”“TLS1.2”基礎(chǔ)之上的,比 HTTP/1、HTTPS 復(fù)雜了一些。
????????雖然 HTTP/2 的底層實(shí)現(xiàn)很復(fù)雜,但它的“語(yǔ)義”還是簡(jiǎn)單的 HTTP/1,之前學(xué)習(xí)的知識(shí)不會(huì)過(guò)時(shí),仍然能夠用得上。
????????? 你可能還會(huì)注意到 URI 里的一個(gè)小變化,端口使用的是“8443”而不是“443”。這是因?yàn)?443 端口已經(jīng)被 HTTPS 協(xié)議占用,Nginx 不允許在同一個(gè)端口上根據(jù)域名選擇性開(kāi)啟 HTTP/2,所以就不得不改用了“8443”。
????????2、HTTP/2內(nèi)核剖析
????????(1)連接前言
????????? 由于 HTTP/2“事實(shí)上”是基于 TLS,所以在正式收發(fā)數(shù)據(jù)之前,會(huì)有 TCP 握手和 TLS 握手,TLS 握手成功之后,客戶端必須要發(fā)送一個(gè)“連接前言”(connection preface),用來(lái)確認(rèn)建立 HTTP/2 連接。
????????? 這個(gè)“連接前言”是標(biāo)準(zhǔn)的 HTTP/1 請(qǐng)求報(bào)文,使用純文本的 ASCII 碼格式,請(qǐng)求方法是特別注冊(cè)的一個(gè)關(guān)鍵字“PRI”,全文只有 24 個(gè)字節(jié):
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n?
????????在 Wireshark 里,HTTP/2 的“連接前言”被稱為“Magic”,意思就是“不可知的魔法”。只要服務(wù)器收到這個(gè)“有魔力的字符串”,就知道客戶端在 TLS 上想要的是 HTTP/2 協(xié)議,而不是其他別的協(xié)議,后面就會(huì)都使用 HTTP/2的數(shù)據(jù)格式。
????????(2)頭部壓縮
????????? 確立了連接之后,HTTP/2 就開(kāi)始準(zhǔn)備請(qǐng)求報(bào)文。因?yàn)檎Z(yǔ)義上它與 HTTP/1 兼容,所以報(bào)文還是由“Header+Body”構(gòu)成的,但在請(qǐng)求發(fā)送前,必須要用“HPACK”算法來(lái)壓縮頭部數(shù)據(jù)。
????????? “HPACK”算法是專門(mén)為壓縮 HTTP 頭部定制的算法,與 gzip、zlib 等壓縮算法不同,它是一個(gè)“有狀態(tài)”的算法,需要客戶端和服務(wù)器各自維護(hù)一份“索引表”,也可以說(shuō)是“字典”(這有點(diǎn)類似 brotli),壓縮和解壓縮就是查表和更新表的操作。
?????????為了方便管理和壓縮,HTTP/2 廢除了原有的起始行概念,把起始行里面的請(qǐng)求方法、URI、狀態(tài)碼等統(tǒng)一轉(zhuǎn)換成了頭字段的形式,并且給這些“不是頭字段的頭字段”起了個(gè)特別的名字——“偽頭字段”(pseudo-header fields)。而起始行里的版本號(hào)和錯(cuò)誤原因短語(yǔ)因?yàn)闆](méi)什么大用,順便也給廢除了。
????????為了與“真頭字段”區(qū)分開(kāi)來(lái),這些“偽頭字段”會(huì)在名字前加一個(gè)“:”,比如“:authority” “:method” “:status”,分別表示的是域名、請(qǐng)求方法和狀態(tài)碼。
????????現(xiàn)在 HTTP 報(bào)文頭就簡(jiǎn)單了,全都是“Key-Value”形式的字段,于是 HTTP/2 就為一些最常用的頭字段定義了一個(gè)只讀的“靜態(tài)表”(Static Table)。
????????下面的這個(gè)表格列出了“靜態(tài)表”的一部分,這樣只要查表就可以知道字段名和對(duì)應(yīng)的值,比如數(shù)字“2”代表“GET”,數(shù)字“8”代表狀態(tài)碼 200。
????????但如果表里只有 Key 沒(méi)有 Value,或者是自定義字段根本找不到該怎么辦呢?這就要用到“動(dòng)態(tài)表”(Dynamic Table),它添加在靜態(tài)表后面,結(jié)構(gòu)相同,但會(huì)在編碼解碼的時(shí)候隨時(shí)更新。
????????? 比如說(shuō),第一次發(fā)送請(qǐng)求時(shí)的“user-agent”字段長(zhǎng)是一百多個(gè)字節(jié),用哈夫曼壓縮編碼發(fā)送之后,客戶端和服務(wù)器都更新自己的動(dòng)態(tài)表,添加一個(gè)新的索引號(hào)“65”。那么下一次發(fā)送的時(shí)候就不用再重復(fù)發(fā)那么多字節(jié)了,只要用一個(gè)字節(jié)發(fā)送編號(hào)就好。
????????? 隨著在 HTTP/2 連接上發(fā)送的報(bào)文越來(lái)越多,兩邊的“字典”也會(huì)越來(lái)越豐富,最終每次的頭部字段都會(huì)變成一兩個(gè)字節(jié)的代碼,原來(lái)上千字節(jié)的頭用幾十個(gè)字節(jié)就可以表示了,壓縮效果比 gzip 要好得多。
????????(3)二進(jìn)制幀
????????? 頭部數(shù)據(jù)壓縮之后,HTTP/2 就要把報(bào)文拆成二進(jìn)制的幀準(zhǔn)備發(fā)送。HTTP/2 的幀結(jié)構(gòu)有點(diǎn)類似 TCP 的段或者 TLS 里的記錄,但報(bào)頭很小,只有 9 字節(jié),非常地節(jié)省(可以對(duì)比一下 TCP 頭,它最少是 20 個(gè)字節(jié))。
????????? 二進(jìn)制的格式也保證了不會(huì)有歧義,而且使用位運(yùn)算能夠非常簡(jiǎn)單高效地解析。
????????? 楨長(zhǎng)度:幀開(kāi)頭是 3 個(gè)字節(jié)的長(zhǎng)度(但不包括頭的 9 個(gè)字節(jié)),默認(rèn)上限是 2^14,最大是 2^24,也就是說(shuō) HTTP/2 的幀通常不超過(guò) 16K,最大是 16M。
????????? 幀類型:大致可以分成數(shù)據(jù)幀和控制幀兩類,HEADERS 幀和 DATA幀屬于數(shù)據(jù)幀,存放的是 HTTP 報(bào)文,而 SETTINGS、PING、PRIORITY 等則是用來(lái)管理流的控制幀。HTTP/2 總共定義了 10 種類型的幀,但一個(gè)字節(jié)可以表示最多 256 種,所以也允許在標(biāo)準(zhǔn)之外定義其他類型實(shí)現(xiàn)功能擴(kuò)展。這就有點(diǎn)像 TLS 里擴(kuò)展協(xié)議的意思了,比如 Google 的gRPC 就利用了這個(gè)特點(diǎn),定義了幾種自用的新幀類型。
????????? 幀標(biāo)志:可以保存 8 個(gè)標(biāo)志位,攜帶簡(jiǎn)單的控制信息。常用的標(biāo)志位有END_HEADERS表示頭數(shù)據(jù)結(jié)束,相當(dāng)于 HTTP/1 里頭后的空行(“\r\n”),END_STREAM表示單方向數(shù)據(jù)發(fā)送結(jié)束(即 EOS,End of Stream),相當(dāng)于 HTTP/1 里 Chunked 分塊結(jié)束標(biāo)志(“0\r\n\r\n”)。
????????? 流標(biāo)識(shí)符:也就是幀所屬的“流”,接收方使用它就可以從亂序的幀里識(shí)別出具有相同流 ID 的幀序列,按順序組裝起來(lái)就實(shí)現(xiàn)了虛擬的“流”。流標(biāo)識(shí)符雖然有 4 個(gè)字節(jié),但最高位被保留不用,所以只有 31 位可以使用,也就是說(shuō),流標(biāo)識(shí)符的上限是 2^31,大約是 21 億。
????????? 好了,把二進(jìn)制頭理清楚后,我們來(lái)看一下 Wireshark 抓包的幀實(shí)例:
????????? 楨長(zhǎng)度:開(kāi)頭的三個(gè)字節(jié)是“00010a”,表示數(shù)據(jù)長(zhǎng)度是 266 字節(jié)。
????????? 幀類型:是 1,表示 HEADERS 幀,負(fù)載(payload)里面存放的是被 HPACK 算法壓縮的頭部信息。
????????幀標(biāo)志:標(biāo)志位是 0x25,轉(zhuǎn)換成二進(jìn)制有 3 個(gè)位被置 1。PRIORITY 表示設(shè)置了流的優(yōu)先級(jí),END_HEADERS 表示這一個(gè)幀就是完整的頭數(shù)據(jù),END_STREAM 表示單方向數(shù)據(jù)發(fā)送結(jié)束,后續(xù)再不會(huì)有數(shù)據(jù)幀(即請(qǐng)求報(bào)文完畢,不會(huì)再有 DATA 幀 /Body 數(shù)據(jù))。
????????流標(biāo)識(shí)符:是整數(shù) 1,表示這是客戶端發(fā)起的第一個(gè)流,后面的響應(yīng)數(shù)據(jù)幀也會(huì)是這個(gè) ID,也就是說(shuō)在 stream[1] 里完成這個(gè)請(qǐng)求響應(yīng)。
????????(4)流與多路復(fù)用
????????? 弄清楚了幀結(jié)構(gòu)后我們就來(lái)看 HTTP/2 的流與多路復(fù)用,它是 HTTP/2 最核心的部分。流是二進(jìn)制幀的雙向傳輸序列。要搞明白流,關(guān)鍵是要理解幀頭里的流 ID。
????????? 在 HTTP/2 連接上,雖然幀是亂序收發(fā)的,但只要它們都擁有相同的流 ID,就都屬于一個(gè)流,而且在這個(gè)流里幀不是無(wú)序的,而是有著嚴(yán)格的先后順序。
????????比如在這次的 Wireshark 抓包里,就有“0、1、3”一共三個(gè)流,實(shí)際上就是分配了三個(gè)流 ID 號(hào),把這些幀按編號(hào)分組,再排一下隊(duì),就成了流。
????????? 在概念上,一個(gè) HTTP/2 的流就等同于一個(gè) HTTP/1 里的“請(qǐng)求 - 應(yīng)答”。在 HTTP/1 里一個(gè)“請(qǐng)求 - 響應(yīng)”報(bào)文來(lái)回是一次 HTTP 通信,在 HTTP/2 里一個(gè)流也承載了相同的功能。
????????? 你還可以對(duì)照著 TCP 來(lái)理解。TCP 運(yùn)行在 IP 之上,其實(shí)從 MAC 層、IP 層的角度來(lái)看,TCP 的“連接”概念也是“虛擬”的。但從功能上看,無(wú)論是 HTTP/2 的流,還是 TCP 的連接,都是實(shí)際存在的,所以你以后大可不必再糾結(jié)于流的“虛擬”性,把它當(dāng)做是一個(gè)真實(shí)存在的實(shí)體來(lái)理解就好。
????????? HTTP/2 的流有如下特點(diǎn):
- 流是可并發(fā)的,一個(gè) HTTP/2 連接上可以同時(shí)發(fā)出多個(gè)流傳輸數(shù)據(jù),也就是并發(fā)多請(qǐng)求,實(shí)現(xiàn)“多路復(fù)用”;
- 客戶端和服務(wù)器都可以創(chuàng)建流,雙方互不干擾;
- 流是雙向的,一個(gè)流里面客戶端和服務(wù)器都可以發(fā)送或接收數(shù)據(jù)幀,也就是一個(gè)“請(qǐng)求- 應(yīng)答”來(lái)回;
- 流之間沒(méi)有固定關(guān)系,彼此獨(dú)立,但流內(nèi)部的幀是有嚴(yán)格順序的;
- 流可以設(shè)置優(yōu)先級(jí),讓服務(wù)器優(yōu)先處理,比如先傳 HTML/CSS,后傳圖片,優(yōu)化用戶體驗(yàn);
- 流 ID 不能重用,只能順序遞增,客戶端發(fā)起的 ID 是奇數(shù),服務(wù)器端發(fā)起的 ID 是偶數(shù);
- 在流上發(fā)送“RST_STREAM”幀可以隨時(shí)終止流,取消接收或發(fā)送;
- 第 0 號(hào)流比較特殊,不能關(guān)閉,也不能發(fā)送數(shù)據(jù)幀,只能發(fā)送控制幀,用于流量控制。
????????? 這里我又畫(huà)了一張圖,把上次的圖略改了一下,顯示了連接中無(wú)序的幀是如何依據(jù)流 ID 重組成流的。
????????從這些特性中,我們還可以推理出一些深層次的知識(shí)點(diǎn)。
????????? 比如說(shuō),HTTP/2 在一個(gè)連接上使用多個(gè)流收發(fā)數(shù)據(jù),那么它本身默認(rèn)就會(huì)是長(zhǎng)連接,所以永遠(yuǎn)不需要“Connection”頭字段(keepalive 或 close)。
????????? 又比如,下載大文件的時(shí)候想取消接收,在 HTTP/1 里只能斷開(kāi) TCP 連接重新“三次握手”,成本很高,而在 HTTP/2 里就可以簡(jiǎn)單地發(fā)送一個(gè)“RST_STREAM”中斷流,而長(zhǎng)連接會(huì)繼續(xù)保持。
?????????再比如,因?yàn)榭蛻舳撕头?wù)器兩端都可以創(chuàng)建流,而流 ID 有奇數(shù)偶數(shù)和上限的區(qū)分,所以大多數(shù)的流 ID 都會(huì)是奇數(shù),而且客戶端在一個(gè)連接里最多只能發(fā)出 2^30,也就是 10 億個(gè)請(qǐng)求。
?????????所以就要問(wèn)了:ID 用完了該怎么辦呢?這個(gè)時(shí)候可以再發(fā)一個(gè)控制幀“GOAWAY”,真正關(guān)閉 TCP 連接。
????????(5)流狀態(tài)轉(zhuǎn)換
????????? 流很重要,也很復(fù)雜。為了更好地描述運(yùn)行機(jī)制,HTTP/2 借鑒了 TCP,根據(jù)幀的標(biāo)志位實(shí)現(xiàn)流狀態(tài)轉(zhuǎn)換。當(dāng)然,這些狀態(tài)也是虛擬的,只是為了輔助理解。
????????? HTTP/2 的流也有一個(gè)狀態(tài)轉(zhuǎn)換圖,雖然比 TCP 要簡(jiǎn)單一點(diǎn),但也不那么好懂,所以今天我只畫(huà)了一個(gè)簡(jiǎn)化的圖,對(duì)應(yīng)到一個(gè)標(biāo)準(zhǔn)的 HTTP“請(qǐng)求 - 應(yīng)答”。? ?
????????? 最開(kāi)始的時(shí)候流都是“空閑”(idle)狀態(tài),也就是“不存在”,可以理解成是待分配的“號(hào)段資源”。
????????? 當(dāng)客戶端發(fā)送 HEADERS 幀后,有了流 ID,流就進(jìn)入了“打開(kāi)”狀態(tài),兩端都可以收發(fā)數(shù)據(jù),然后客戶端發(fā)送一個(gè)帶“END_STREAM”標(biāo)志位的幀,流就進(jìn)入了“半關(guān)閉”狀態(tài)。
????????這個(gè)“半關(guān)閉”狀態(tài)很重要,意味著客戶端的請(qǐng)求數(shù)據(jù)已經(jīng)發(fā)送完了,需要接受響應(yīng)數(shù)據(jù),而服務(wù)器端也知道請(qǐng)求數(shù)據(jù)接收完畢,之后就要內(nèi)部處理,再發(fā)送響應(yīng)數(shù)據(jù)。
?????????響應(yīng)數(shù)據(jù)發(fā)完了之后,也要帶上“END_STREAM”標(biāo)志位,表示數(shù)據(jù)發(fā)送完畢,這樣流兩端就都進(jìn)入了“關(guān)閉”狀態(tài),流就結(jié)束了。
????????剛才也說(shuō)過(guò),流 ID 不能重用,所以流的生命周期就是 HTTP/1 里的一次完整的“請(qǐng)求 - 應(yīng)答”,流關(guān)閉就是一次通信結(jié)束。
????????下一次再發(fā)請(qǐng)求就要開(kāi)一個(gè)新流(而不是新連接),流 ID 不斷增加,直到到達(dá)上限,發(fā)送“GOAWAY”幀開(kāi)一個(gè)新的 TCP 連接,流 ID 就又可以重頭計(jì)數(shù)。
????????你再看看這張圖,是不是和 HTTP/1 里的標(biāo)準(zhǔn)“請(qǐng)求 - 應(yīng)答”過(guò)程很像,只不過(guò)這是發(fā)生在虛擬的“流”上,而不是實(shí)際的 TCP 連接,又因?yàn)榱骺梢圆l(fā),所以 HTTP/2 就可以實(shí)現(xiàn)無(wú)阻塞的多路復(fù)用。
????????3、HTTP/3
????????(1)HTTP/2的隊(duì)頭阻塞
????????? HTTP/2 雖然使用“幀”“流”“多路復(fù)用”,沒(méi)有了“隊(duì)頭阻塞”,但這些手段都是在應(yīng)用層里,而在下層,也就是 TCP 協(xié)議里,還是會(huì)發(fā)生“隊(duì)頭阻塞”。
????????? HTTP/2 把多個(gè)“請(qǐng)求 - 響應(yīng)”分解成流,交給TCP 后,TCP 會(huì)再拆成更小的包依次發(fā)送(其實(shí)在 TCP 里應(yīng)該叫 segment,也就是“段”)。
????????在網(wǎng)絡(luò)良好的情況下,包可以很快送達(dá)目的地。但如果網(wǎng)絡(luò)質(zhì)量比較差,像手機(jī)上網(wǎng)的時(shí)候,就有可能會(huì)丟包。而 TCP 為了保證可靠傳輸,有個(gè)特別的“丟包重傳”機(jī)制,丟失的包必須要等待重新傳輸確認(rèn),其他的包即使已經(jīng)收到了,也只能放在緩沖區(qū)里,上層的應(yīng)用拿不出來(lái),只能“干著急”。
????????? 我舉個(gè)簡(jiǎn)單的例子:
????????? 客戶端用 TCP 發(fā)送了三個(gè)包,但服務(wù)器所在的操作系統(tǒng)只收到了后兩個(gè)包,第一個(gè)包丟了。那么內(nèi)核里的 TCP 協(xié)議棧就只能把已經(jīng)收到的包暫存起來(lái),“停下”等著客戶端重傳那個(gè)丟失的包,這樣就又出現(xiàn)了“隊(duì)頭阻塞”。
????????由于這種“隊(duì)頭阻塞”是 TCP 協(xié)議固有的,所以 HTTP/2 即使設(shè)計(jì)出再多的“花樣”也無(wú)法解決。Google 在推 SPDY 的時(shí)候就已經(jīng)意識(shí)到了這個(gè)問(wèn)題,于是就又發(fā)明了一個(gè)新的“QUIC”協(xié)議,讓 HTTP 跑在 QUIC 上而不是 TCP 上。
????????而這個(gè)“HTTP over QUIC”就是 HTTP 協(xié)議的下一個(gè)大版本,HTTP/3。它在 HTTP/2 的基礎(chǔ)上又實(shí)現(xiàn)了質(zhì)的飛躍,真正“完美”地解決了“隊(duì)頭阻塞”問(wèn)題。
????????這里先貼一下 HTTP/3 的協(xié)議棧圖,讓你對(duì)它有個(gè)大概的了解。
????????(2)QUIC協(xié)議
????????? 從這張圖里,你可以看到 HTTP/3 有一個(gè)關(guān)鍵的改變,那就是它把下層的 TCP“抽掉”了,換成了 UDP。因?yàn)?UDP 是無(wú)序的,包之間沒(méi)有依賴關(guān)系,所以就從根本上解決了“隊(duì)頭阻塞”。
????????? UDP 是一個(gè)簡(jiǎn)單、不可靠的傳輸協(xié)議,只是對(duì) IP 協(xié)議的一層很薄的包裝,和TCP 相比,它實(shí)際應(yīng)用的較少。不過(guò)正是因?yàn)樗?jiǎn)單,不需要建連和斷連,通信成本低,也就非常靈活、高效,“可塑性”很強(qiáng)。
????????所以,QUIC 就選定了 UDP,在它之上把 TCP 的那一套連接管理、擁塞窗口、流量控制等“搬”了過(guò)來(lái),“去其糟粕,取其精華”,打造出了一個(gè)全新的可靠傳輸協(xié)議,可以認(rèn)為是“新時(shí)代的 TCP”。
????????? QUIC 最早是由 Google 發(fā)明的,被稱為 gQUIC。而當(dāng)前正在由 IETF 標(biāo)準(zhǔn)化的 QUIC 被稱為 iQUIC。兩者的差異非常大,甚至比當(dāng)年的 SPDY 與 HTTP/2 的差異還要大。
? ? ? ? ?gQUIC 混合了 UDP、TLS、HTTP,是一個(gè)應(yīng)用層的協(xié)議。而 IETF 則對(duì) gQUIC 做了“清理”,把應(yīng)用部分分離出來(lái),形成了 HTTP/3,原來(lái)的 UDP 部分“下放”到了傳輸層,所以 iQUIC 有時(shí)候也叫“QUIC-transport”。
????????? 接下來(lái)要說(shuō)的 QUIC 都是指 iQUIC,要記住,它與早期的 gQUIC 不同,是一個(gè)傳輸層的協(xié)議,和 TCP 是平級(jí)的。
????????(3)QUIC的特點(diǎn)
????????? QUIC 基于 UDP,而 UDP 是“無(wú)連接”的,根本就不需要“握手”和“揮手”,所以天生就要比 TCP 快。
????????就像 TCP 在 IP 的基礎(chǔ)上實(shí)現(xiàn)了可靠傳輸一樣,QUIC 也基于 UDP 實(shí)現(xiàn)了可靠傳輸,保證數(shù)據(jù)一定能夠抵達(dá)目的地。它還引入了類似 HTTP/2 的“流”和“多路復(fù)用”,單個(gè)“流”是有序的,可能會(huì)因?yàn)閬G包而阻塞,但其他“流”不會(huì)受到影響。
?????????為了防止網(wǎng)絡(luò)上的中間設(shè)備(Middle Box)識(shí)別協(xié)議的細(xì)節(jié),QUIC 全面采用加密通信,可以很好地抵御竄改和“協(xié)議僵化”(ossification)。
?????????而且,因?yàn)?TLS1.3 已經(jīng)在2018年正式發(fā)布,所以 QUIC 就直接應(yīng)用了 TLS1.3,順便也就獲得了 0-RTT、1-RTT 連接的好處。
????????但 QUIC 并不是建立在 TLS 之上,而是內(nèi)部“包含”了 TLS。它使用自己的幀“接管”了TLS 里的“記錄”,握手消息、警報(bào)消息都不使用 TLS 記錄,直接封裝成 QUIC 的幀發(fā)送,省掉了一次開(kāi)銷(xiāo)。
????????(4)QUIC內(nèi)部細(xì)節(jié)
????????? 由于 QUIC 在協(xié)議棧里比較偏底層,所以我只簡(jiǎn)略介紹兩個(gè)內(nèi)部的關(guān)鍵知識(shí)點(diǎn)。
????????? QUIC 的基本數(shù)據(jù)傳輸單位是包(packet)和幀(frame),一個(gè)包由多個(gè)幀組成,包面向的是“連接”,幀面向的是“流”。
????????QUIC 使用不透明的“連接 ID”來(lái)標(biāo)記通信的兩個(gè)端點(diǎn),客戶端和服務(wù)器可以自行選擇一組 ID 來(lái)標(biāo)記自己,這樣就解除了 TCP 里連接對(duì)“IP 地址 + 端口”(即常說(shuō)的四元組)的強(qiáng)綁定,支持“連接遷移”(Connection Migration)。
?????????比如你下班回家,手機(jī)會(huì)自動(dòng)由 4G 切換到 WiFi。這時(shí) IP 地址會(huì)發(fā)生變化,TCP 就必須重新建立連接。而 QUIC 連接里的兩端連接 ID 不會(huì)變,所以連接在“邏輯上”沒(méi)有中斷,它就可以在新的 IP 地址上繼續(xù)使用之前的連接,消除重連的成本,實(shí)現(xiàn)連接的無(wú)縫遷移。
????????? QUIC 的幀里有多種類型,PING、ACK 等幀用于管理連接,而 STREAM 幀專門(mén)用來(lái)實(shí)現(xiàn)流。
?????????QUIC 里的流與 HTTP/2 的流非常相似,也是幀的序列,你可以對(duì)比著來(lái)理解。但 HTTP/2里的流都是雙向的,而 QUIC 則分為雙向流和單向流。
????????? QUIC 幀普遍采用變長(zhǎng)編碼,最少只要 1 個(gè)字節(jié),最多有 8 個(gè)字節(jié)。流 ID 的最大可用位數(shù)是 62,數(shù)量上比 HTTP/2 的 2^31 大大增加。
????????? 流 ID 還保留了最低兩位用作標(biāo)志,第 1 位標(biāo)記流的發(fā)起者,0 表示客戶端,1 表示服務(wù)器;第 2 位標(biāo)記流的方向,0 表示雙向流,1 表示單向流。所以 QUIC 流 ID 的奇偶性質(zhì)和 HTTP/2 剛好相反,客戶端的 ID 是偶數(shù),從 0 開(kāi)始計(jì)數(shù)。
????????(5)HTTP/3協(xié)議
????????? 因?yàn)?QUIC 本身就已經(jīng)支持了加密、流和多路復(fù)用,所以 HTTP/3 的工作減輕了很多,把流控制都交給 QUIC 去做。調(diào)用的不再是 TLS 的安全接口,也不是 Socket API,而是專門(mén)的 QUIC 函數(shù)。不過(guò)這個(gè)“QUIC 函數(shù)”還沒(méi)有形成標(biāo)準(zhǔn),必須要綁定到某一個(gè)具體的實(shí)現(xiàn)庫(kù)。
????????? HTTP/3 里仍然使用流來(lái)發(fā)送“請(qǐng)求 - 響應(yīng)”,但它自身不需要像 HTTP/2 那樣再去定義流,而是直接使用 QUIC 的流,相當(dāng)于做了一個(gè)“概念映射”。
????????HTTP/3 里的“雙向流”可以完全對(duì)應(yīng)到 HTTP/2 的流,而“單向流”在 HTTP/3 里用來(lái)實(shí)現(xiàn)控制和推送,近似地對(duì)應(yīng) HTTP/2 的 0 號(hào)流。
????????? 由于流管理被“下放”到了 QUIC,所以 HTTP/3 里幀的結(jié)構(gòu)也變簡(jiǎn)單了。
?????????HTTP/3 里的幀仍然分成數(shù)據(jù)幀和控制幀兩類,HEADERS 幀和 DATA 幀傳輸數(shù)據(jù),但其他一些幀因?yàn)樵谙聦拥?QUIC 里有了替代,所以在 HTTP/3 里就都消失了,比如RST_STREAM、WINDOW_UPDATE、PING 等。
????????? 頭部壓縮算法在 HTTP/3 里升級(jí)成了“QPACK”,使用方式上也做了改變。雖然也分成靜態(tài)表和動(dòng)態(tài)表,但在流上發(fā)送 HEADERS 幀時(shí)不能更新字段,只能引用,索引表的更新需要在專門(mén)的單向流上發(fā)送指令來(lái)管理,解決了 HPACK 的“隊(duì)頭阻塞”問(wèn)題。
?????????另外,QPACK 的字典也做了優(yōu)化,靜態(tài)表由之前的 61 個(gè)增加到了 98 個(gè),而且序號(hào)從 0開(kāi)始,也就是說(shuō)“:authority”的編號(hào)是 0。
????????(6)HTTP/3服務(wù)發(fā)現(xiàn)
????????? HTTP/3 沒(méi)有指定默認(rèn)的端口號(hào),也就是說(shuō)不一定非要在 UDP 的 80 或者 443 上提供 HTTP/3 服務(wù)。
????????? 這就要用到 HTTP/2 里的“擴(kuò)展幀”了。瀏覽器需要先用 HTTP/2 協(xié)議連接服務(wù)器,然后服務(wù)器可以在啟動(dòng) HTTP/2 連接后發(fā)送一個(gè)“Alt-Svc”幀,包含一個(gè)“h3=host:port”的字符串,告訴瀏覽器在另一個(gè)端點(diǎn)上提供等價(jià)的 HTTP/3 服務(wù)。
????????瀏覽器收到“Alt-Svc”幀,會(huì)使用 QUIC 異步連接指定的端口,如果連接成功,就會(huì)斷開(kāi)HTTP/2 連接,改用新的 HTTP/3 收發(fā)數(shù)據(jù)。
????????4、是否要遷移到HTTP/2
????????? 與各大瀏覽器“強(qiáng)推”HTTPS 的待遇不一樣,HTTP/2 的公布可謂是“波瀾不驚”。雖然它是 HTTP 協(xié)議的一個(gè)重大升級(jí),但 Apple、Google 等科技巨頭并沒(méi)有像 HTTPS 那樣給予大量資源的支持。
????????? 直到今天,HTTP/2 在互聯(lián)網(wǎng)上還是處于“不溫不火”的狀態(tài),雖然已經(jīng)有了不少的網(wǎng)站改造升級(jí)到了 HTTP/2,但普及的速度遠(yuǎn)不及 HTTPS。
????????(1)HTTP/2的優(yōu)點(diǎn)
????????? 兼容:HTTP/2 最大的一個(gè)優(yōu)點(diǎn)是完全保持了與 HTTP/1 的兼容,在語(yǔ)義上沒(méi)有任何變化,因?yàn)榧嫒?HTTP/1,所以 HTTP/2 也具有 HTTP/1 的所有優(yōu)點(diǎn),并且“基本”解決了HTTP/1 的所有缺點(diǎn)。
? ? ? ??安全:HTTP/2 對(duì) HTTPS 在各方面都做了強(qiáng)化。下層的 TLS 至少是 1.2,而且只能使用前向安全的密碼套件(即 ECDHE),這同時(shí)也就默認(rèn)實(shí)現(xiàn)了“TLS False Start”,支持1-RTT 握手,所以不需要再加額外的配置就可以自動(dòng)實(shí)現(xiàn) HTTPS 加速。
? ?????? 性能:影響網(wǎng)絡(luò)速度的兩個(gè)關(guān)鍵因素是“帶寬”和“延遲”,HTTP/2 的頭部壓縮、多路復(fù)用、流優(yōu)先級(jí)、服務(wù)器推送等手段其實(shí)都是針對(duì)這兩個(gè)要點(diǎn)。
- 頭部壓縮:節(jié)約帶寬的基本手段就是壓縮,在 HTTP/1 里只能壓縮 body,而 HTTP/2 則可以用HPACK 算法壓縮 header,這對(duì)高流量的網(wǎng)站非常有價(jià)值,有數(shù)據(jù)表明能節(jié)省大概5%~10% 的流量,這是實(shí)實(shí)在在的“真金白銀”。
- 多路復(fù)用:與 HTTP/1“并發(fā)多個(gè)連接”不同,HTTP/2 的“多路復(fù)用”特性要求對(duì)一個(gè)域名(或者IP)只用一個(gè) TCP 連接,所有的數(shù)據(jù)都在這一個(gè)連接上傳輸,這樣不僅節(jié)約了客戶端、服務(wù)器和網(wǎng)絡(luò)的資源,還可以把帶寬跑滿,讓 TCP 充分“吃飽”。HTTP/1 里的長(zhǎng)連接,雖然是雙向通信,但任意一個(gè)時(shí)間點(diǎn)實(shí)際上還是單向的,再加上“隊(duì)頭阻塞”,實(shí)際的帶寬打了個(gè)“對(duì)折”還不止。HTTP/2 里,“多路復(fù)用”則讓 TCP 開(kāi)足了馬力,“全速狂奔”,多個(gè)請(qǐng)求響應(yīng)并發(fā),每時(shí)每刻上下行方向上都有流在傳輸數(shù)據(jù),沒(méi)有空閑的時(shí)候,帶寬的利用率能夠接近100%。所以,HTTP/2 只使用一個(gè)連接,就能抵得過(guò) HTTP/1 里的五六個(gè)連接。
- 流優(yōu)先級(jí):不過(guò)流也可能會(huì)有依賴關(guān)系,可能會(huì)存在等待導(dǎo)致的阻塞,這就是“延遲”,所以 HTTP/2的其他特性就派上了用場(chǎng)?!皟?yōu)先級(jí)”可以讓客戶端告訴服務(wù)器,哪個(gè)文件更重要,更需要優(yōu)先傳輸,服務(wù)器就可以調(diào)高流的優(yōu)先級(jí),合理地分配有限的帶寬資源,讓高優(yōu)先級(jí)的 HTML、圖片更快地到達(dá)客戶端,盡早加載顯示。
- 服務(wù)器推送:也是降低延遲的有效手段,它不需要客戶端預(yù)先請(qǐng)求,服務(wù)器直接就發(fā)給客戶端,這就省去了客戶端解析 HTML 再請(qǐng)求的時(shí)間。
????????(2)HTTP/2的缺點(diǎn)
????????? HTTP/2 在 TCP 級(jí)別還是存在“隊(duì)頭阻塞”的問(wèn)題。所以,如果網(wǎng)絡(luò)連接質(zhì)量差,發(fā)生丟包,那么 TCP 會(huì)等待重傳,傳輸速度就會(huì)降低。
????????? 在移動(dòng)網(wǎng)絡(luò)中發(fā)生 IP 地址切換的時(shí)候,下層的 TCP 必須重新建連,要再次“握手”,經(jīng)歷“慢啟動(dòng)”,而且之前連接里積累的 HPACK 字典也都消失了,必須重頭開(kāi)始計(jì)算,導(dǎo)致帶寬浪費(fèi)和時(shí)延。
?????????HTTP/2 對(duì)一個(gè)域名只開(kāi)一個(gè)連接,所以一旦這個(gè)連接出問(wèn)題,那么整個(gè)網(wǎng)站的體驗(yàn)也就變差了。而這些情況下 HTTP/1 反而不會(huì)受到影響,因?yàn)樗氨緛?lái)就慢”,而且還會(huì)對(duì)一個(gè)域名開(kāi)6~8 個(gè)連接,頂多其中的一兩個(gè)連接會(huì)“更慢”,其他的連接不會(huì)受到影響。
????????(3)應(yīng)該遷移到HTTP/2嗎
????????? HTTP/2 處于一個(gè)略“尷尬”的位置,前面有“老前輩”HTTP/1,后面有“新來(lái)者”HTTP/3,即有“老前輩”的“打壓”,又有“新來(lái)者”的“追趕”,也就難怪沒(méi)有獲得市場(chǎng)的大力“吹捧”了。
????????? 但這絕不是說(shuō) HTTP/2“一無(wú)是處”,實(shí)際上 HTTP/2 的性能改進(jìn)效果是非常明顯的,Top1000 的網(wǎng)站中已經(jīng)有超過(guò) 40% 運(yùn)行在了 HTTP/2 上,包括知名的 Apple、Facebook、Google、Twitter 等等。僅用了四年的時(shí)間,HTTP/2 就擁有了這么大的市場(chǎng)份額和巨頭的認(rèn)可,足以證明它的價(jià)值。
????????? 因?yàn)?HTTP/2 的側(cè)重點(diǎn)是“性能”,所以“是否遷移”就需要在這方面進(jìn)行評(píng)估。如果網(wǎng)站的流量很大,那么 HTTP/2 就可以帶來(lái)可觀的收益;反之,如果網(wǎng)站流量比較小,那么級(jí)到 HTTP/2 就沒(méi)有太多必要了,只要利用現(xiàn)有的 HTTP 再優(yōu)化就足矣。
????????不過(guò)如果你是新建網(wǎng)站,我覺(jué)得完全可以跳過(guò) HTTP/1、HTTPS,直接“一步到位”,上HTTP/2,這樣不僅可以獲得性能提升,還免去了老舊的“歷史包袱”,日后也不會(huì)再有遷移的煩惱。
????????(4)配置HTTP/2
????????? 因?yàn)?HTTP/2“事實(shí)上”是加密的,所以如果你已經(jīng)成功遷移到了HTTPS,那么在 Nginx 里啟用 HTTP/2 簡(jiǎn)直可以說(shuō)是“不費(fèi)吹灰之力”,只需要在 server配置里再多加一個(gè)參數(shù)就可以搞定了。
server {
?? ?listen 443 ssl http2;
?? ?server_name www.xxx.net;
?? ?ssl_certificate xxx.crt;
?? ?ssl_certificate_key xxx.key;
????????? 注意“l(fā)isten”指令,在“ssl”后面多了一個(gè)“http2”,這就表示在 443 端口上開(kāi)啟了SSL 加密,然后再啟用HTTP/2。
????????? 配置服務(wù)器推送特性可以使用指令“http2_push”和“http2_push_preload”:
http2_push /style/xxx.css;
http2_push_preload on;
????????? 不過(guò)如何合理地配置推送是個(gè)難題,如果推送給瀏覽器不需要的資源,反而浪費(fèi)了帶寬。這方面暫時(shí)沒(méi)有一般性的原則指導(dǎo),你必須根據(jù)自己網(wǎng)站的實(shí)際情況去“猜測(cè)”客戶端最需要的數(shù)據(jù)。
????????? 優(yōu)化方面,HTTPS 的一些策略依然適用,比如精簡(jiǎn)密碼套件、ECC 證書(shū)、會(huì)話復(fù)用、HSTS 減少重定向跳轉(zhuǎn)等等。但還有一些優(yōu)化手段在 HTTP/2 里是不適用的,而且還會(huì)有反效果,比如說(shuō)常見(jiàn)的精靈圖(Spriting)、資源內(nèi)聯(lián)(inlining)、域名分片(Sharding)等。
?????????還要注意一點(diǎn),HTTP/2 默認(rèn)啟用 header 壓縮(HPACK),但并沒(méi)有默認(rèn)啟用 body 壓縮,所以不要忘了在 Nginx 配置文件里加上“gzip”指令,壓縮 HTML、JS 等文本數(shù)據(jù)。
????????(5)應(yīng)用層協(xié)議協(xié)商(ALPN)
????????? 在 URI 里用的都是 HTTPS 協(xié)議名,沒(méi)有版本標(biāo)記,瀏覽器怎么知道服務(wù)器支持 HTTP/2 呢,答案在 TLS 的擴(kuò)展里,有一個(gè)叫“ALPN”(Application Layer Protocol Negotiation)的東西,用來(lái)與服務(wù)器就 TLS 上跑的應(yīng)用協(xié)議進(jìn)行“協(xié)商”。
????????? 客戶端在發(fā)起“Client Hello”握手的時(shí)候,后面會(huì)帶上一個(gè)“ALPN”擴(kuò)展,里面按照優(yōu)先順序列出客戶端支持的應(yīng)用協(xié)議。
?????????就像下圖這樣,最優(yōu)先的是“h2”,其次是“http/1.1”,以前還有“spdy”,以后還可能會(huì)有“h3”。
????????服務(wù)器看到 ALPN 擴(kuò)展以后就可以從列表里選擇一種應(yīng)用協(xié)議,在“Server Hello”里也帶上“ALPN”擴(kuò)展,告訴客戶端服務(wù)器決定使用的是哪一種。因?yàn)槲覀冊(cè)?Nginx 配置里使用了 HTTP/2 協(xié)議,所以在這里它選擇的就是“h2”。
????????? 這樣在 TLS 握手結(jié)束后,客戶端和服務(wù)器就通過(guò)“ALPN”完成了應(yīng)用層的協(xié)議協(xié)商,后面就可以使用 HTTP/2 通信了。
六、優(yōu)化
????????? 從 HTTP 最基本的“請(qǐng)求 - 應(yīng)答”模型來(lái)著手。在這個(gè)模型里有兩個(gè)角色:客戶端和服務(wù)器,還有中間的傳輸鏈路,考查性能就可以看這三個(gè)部分。
? ? ? ?? 但因?yàn)槲覀兪菬o(wú)法完全控制客戶端的,所以實(shí)際上的優(yōu)化工作通常是在服務(wù)器端。這里又可以細(xì)分為后端和前端,后端是指網(wǎng)站的后臺(tái)服務(wù),而前端就是 HTML、CSS、圖片等展現(xiàn)在客戶端的代碼和數(shù)據(jù)。
????????? 總的來(lái)說(shuō),任何計(jì)算機(jī)系統(tǒng)的優(yōu)化都可以分成這么幾類:硬件軟件、內(nèi)部外部、花錢(qián)不花錢(qián)。
?????????投資購(gòu)買(mǎi)現(xiàn)成的硬件最簡(jiǎn)單的優(yōu)化方式,比如換上更強(qiáng)的 CPU、更快的網(wǎng)卡、更大的帶寬、更多的服務(wù)器,效果也會(huì)“立竿見(jiàn)影”,直接提升網(wǎng)站的服務(wù)能力,也就實(shí)現(xiàn)了 HTTP優(yōu)化。
?????????花錢(qián)購(gòu)買(mǎi)外部的軟件或者服務(wù)也是一種行之有效的優(yōu)化方式,最“物有所值”的應(yīng)該算是 CDN。CDN 專注于網(wǎng)絡(luò)內(nèi)容交付,幫助網(wǎng)站解決“中間一公里”的問(wèn)題,還有很多其他非常專業(yè)的優(yōu)化功能。把網(wǎng)站交給 CDN 運(yùn)營(yíng),就好像是“讓網(wǎng)站坐上了噴氣飛機(jī)”,能夠直達(dá)用戶,幾乎不需要費(fèi)什么力氣就能夠達(dá)成很好的優(yōu)化效果。
?????????在網(wǎng)站內(nèi)部、“不花錢(qián)”的軟件優(yōu)化,主要有三種方式:開(kāi)源、節(jié)流、緩存。
????????1、服務(wù)器
????????? 我們先來(lái)看看服務(wù)器,它一般運(yùn)行在 Linux 操作系統(tǒng)上,用 Apache、Nginx 等 Web 服務(wù)器軟件對(duì)外提供服務(wù),所以,性能的含義就是它的服務(wù)能力,也就是盡可能多、盡可能快地處理用戶的請(qǐng)求。
????????? 衡量服務(wù)器性能的主要指標(biāo)有三個(gè):吞吐量(requests per second)、并發(fā)數(shù)(concurrency)和響應(yīng)時(shí)間(time per request)。
- 吞吐量:就是我們常說(shuō)的 RPS,每秒的請(qǐng)求次數(shù),也有叫 TPS、QPS,它是服務(wù)器最基本的性能指標(biāo),RPS 越高就說(shuō)明服務(wù)器的性能越好。
- 并發(fā)數(shù):反映的是服務(wù)器的負(fù)載能力,也就是服務(wù)器能夠同時(shí)支持的客戶端數(shù)量,當(dāng)然也是越多越好,能夠服務(wù)更多的用戶。
- 響應(yīng)時(shí)間:反映的是服務(wù)器的處理能力,也就是快慢程度,響應(yīng)時(shí)間越短,單位時(shí)間內(nèi)服務(wù)器就能夠給越多的用戶提供服務(wù),提高吞吐量和并發(fā)數(shù)。
????????除了上面的三個(gè)基本性能指標(biāo),服務(wù)器還要考慮 CPU、內(nèi)存、硬盤(pán)和網(wǎng)卡等系統(tǒng)資源的占用程度,利用率過(guò)高或者過(guò)低都可能有問(wèn)題。
????????? 在 HTTP 多年的發(fā)展過(guò)程中,已經(jīng)出現(xiàn)了很多成熟的工具來(lái)測(cè)量這些服務(wù)器的性能指標(biāo),開(kāi)源的、商業(yè)的、命令行的、圖形化的都有。在 Linux 上,最常用的性能測(cè)試工具可能就是 ab(Apache Bench)了,比如,下面的命令指定了并發(fā)數(shù) 100,總共發(fā)送 10000 個(gè)請(qǐng)求:
ab -c 100 -n 10000 'http://www.xxx.com'
????????? 系統(tǒng)資源監(jiān)控方面,Linux 自帶的工具也非常多,常用的有 uptime、top、vmstat、netstat、sar 等等:
top # 查看 CPU 和內(nèi)存占用情況
vmstat 2 # 每 2 秒檢查一次系統(tǒng)狀態(tài)
sar -n DEV 2 # 看所有網(wǎng)卡的流量,定時(shí) 2 秒檢查
????????? 理解了這些性能指標(biāo),我們就知道了服務(wù)器的性能優(yōu)化方向:合理利用系統(tǒng)資源,提高服務(wù)器的吞吐量和并發(fā)數(shù),降低響應(yīng)時(shí)間。
????????2、客戶端
????????? 客戶端是信息的消費(fèi)者,一切數(shù)據(jù)都要通過(guò)網(wǎng)絡(luò)從服務(wù)器獲取,所以它最基本的性能指標(biāo)就是“延遲”(latency)。所謂的“延遲”其實(shí)就是“等待”,等待數(shù)據(jù)到達(dá)客戶端時(shí)所花費(fèi)的時(shí)間。但因?yàn)?HTTP 的傳輸鏈路很復(fù)雜,所以延遲的原因也就多種多樣。
- 光速:因?yàn)榈乩砭嚯x而導(dǎo)致的延遲是無(wú)法克服的,訪問(wèn)數(shù)千公里外的網(wǎng)站顯然會(huì)有更大的延遲。
- 帶寬:它又包括接入互聯(lián)網(wǎng)時(shí)的電纜、WiFi、4G 和運(yùn)營(yíng)商內(nèi)部網(wǎng)絡(luò)、運(yùn)營(yíng)商之間網(wǎng)絡(luò)的各種帶寬,每一處都有可能成為數(shù)據(jù)傳輸?shù)钠款i,降低傳輸速度,增加延遲。
- DNS 查詢:如果域名在本地沒(méi)有緩存,就必須向 DNS 系統(tǒng)發(fā)起查詢,引發(fā)一連串的網(wǎng)絡(luò)通信成本,而在獲取 IP 地址之前客戶端只能等待,無(wú)法訪問(wèn)網(wǎng)站。
- TCP 握手:你應(yīng)該對(duì)它比較熟悉了吧,必須要經(jīng)過(guò) SYN、SYN/ACK、ACK三個(gè)包之后才能建立連接,它帶來(lái)的延遲由光速和帶寬共同決定。
?????????對(duì)于 HTTP 性能優(yōu)化,也有一個(gè)專門(mén)的測(cè)試網(wǎng)站“WebPageTest”。它的特點(diǎn)是在世界各地建立了很多的測(cè)試點(diǎn),可以任意選擇地理位置、機(jī)型、操作系統(tǒng)和瀏覽器發(fā)起測(cè)試。網(wǎng)站測(cè)試結(jié)果是一個(gè)直觀的“瀑布圖”(Waterfall Chart),清晰地列出了頁(yè)面中所有資源加載的先后順序和時(shí)間消耗,比如下圖就是對(duì) GitHub 首頁(yè)的一次測(cè)試。
?????????Chrome 等瀏覽器自帶的開(kāi)發(fā)者工具也可以很好地觀察客戶端延遲指標(biāo),面板左邊有每個(gè)URI 具體消耗的時(shí)間,面板的右邊也是類似的瀑布圖。點(diǎn)擊某個(gè) URI,在 Timing 頁(yè)里會(huì)顯示出一個(gè)小型的“瀑布圖”,是這個(gè)資源消耗時(shí)間的詳細(xì)分解,延遲的原因都列的清清楚楚,比如下面的這張圖:
????????? 圖里面的這些指標(biāo)都是什么含義呢?我給你解釋一下:
- Queued at:因?yàn)橛小瓣?duì)頭阻塞”,瀏覽器對(duì)每個(gè)域名最多開(kāi) 6 個(gè)并發(fā)連接(HTTP/1.1),當(dāng)頁(yè)面里鏈接很多的時(shí)候就必須排隊(duì)等待(Queued、Queueing),這里它就等待了 1.62 秒,然后才被瀏覽器正式處理;
- Stalled:瀏覽器要預(yù)先分配資源,調(diào)度連接,花費(fèi)了 11.56 毫秒;
- DNS Lookup:連接前必須要解析域名,這里因?yàn)橛斜镜鼐彺?#xff0c;所以只消耗了 0.41 毫秒;
- Initial connection、SSL:與網(wǎng)站服務(wù)器建立連接的成本很高,總共花費(fèi)了 270.87 毫秒,其中有 134.89 毫秒用于TLS 握手,那么 TCP 握手的時(shí)間就是 135.98 毫秒;
- Request sent:實(shí)際發(fā)送數(shù)據(jù)非???#xff0c;只用了 0.11 毫秒;
- TTFB:之后就是等待服務(wù)器的響應(yīng),專有名詞叫 TTFB(Time To First Byte),也就是“首字節(jié)響應(yīng)時(shí)間”,里面包括了服務(wù)器的處理時(shí)間和網(wǎng)絡(luò)傳輸時(shí)間,花了 124.2 毫秒;
- Content Dowload:接收數(shù)據(jù)也是非??斓?#xff0c;用了 3.58 毫秒。
????????? 從這張圖你可以看到,一次 HTTP“請(qǐng)求 - 響應(yīng)”的過(guò)程中延遲的時(shí)間是非常驚人的,總時(shí)間 415.04 毫秒里占了差不多 99%。所以,客戶端 HTTP 性能優(yōu)化的關(guān)鍵就是:降低延遲
。
????????
????????3、傳輸鏈路
????????? 以 HTTP 基本的“請(qǐng)求 - 應(yīng)答”模型為出發(fā)點(diǎn),剛才我們得到了 HTTP 性能優(yōu)化的一些指標(biāo),現(xiàn)在,我們來(lái)把視角放大到“真實(shí)的世界”,看看客戶端和服務(wù)器之間的傳輸鏈路,它也是影響 HTTP 性能的關(guān)鍵。
????????? 如下是互聯(lián)網(wǎng)示意圖:
- 第一公里:是指網(wǎng)站的出口,也就是服務(wù)器接入互聯(lián)網(wǎng)的傳輸線路,它的帶寬直接決定了網(wǎng)站對(duì)外的服務(wù)能力,也就是吞吐量等指標(biāo)。顯然,優(yōu)化性能應(yīng)該在這“第一公里”加大投入,盡量購(gòu)買(mǎi)大帶寬,接入更多的運(yùn)營(yíng)商網(wǎng)絡(luò)。
- 中間一公里:就是由許多小網(wǎng)絡(luò)組成的實(shí)際的互聯(lián)網(wǎng),其實(shí)它遠(yuǎn)不止“一公里”,而是非常非常龐大和復(fù)雜的網(wǎng)絡(luò),地理距離、網(wǎng)絡(luò)互通都嚴(yán)重影響了傳輸速度。好在這里面有一個(gè)HTTP 的“好幫手”——CDN,它可以幫助網(wǎng)站跨越“千山萬(wàn)水”,讓這段距離看起來(lái)真的就好像只有“一公里”。
- 最后一公里:是用戶訪問(wèn)互聯(lián)網(wǎng)的入口,對(duì)于固網(wǎng)用戶就是光纖、網(wǎng)線,對(duì)于移動(dòng)用戶就是 WiFi、基站。以前它是客戶端性能的主要瓶頸,延遲大帶寬小,但隨著近幾年 4G 和高速寬帶的普及,“最后一公里”的情況已經(jīng)好了很多,不再是制約性能的主要因素了。
- 第零公里:就是網(wǎng)站內(nèi)部的 Web 服務(wù)系統(tǒng)。它其實(shí)也是一個(gè)小型的網(wǎng)絡(luò)(當(dāng)然也可能會(huì)非常大),中間的數(shù)據(jù)處理、傳輸會(huì)導(dǎo)致延遲,增加服務(wù)器的響應(yīng)時(shí)間,也是一個(gè)不可忽視的優(yōu)化點(diǎn)。
????????? 在上面整個(gè)互聯(lián)網(wǎng)傳輸鏈路中,末端的“最后一公里”我們是無(wú)法控制的,所以我們只能在“第零公里”“第一公里”和“中間一公里”這幾個(gè)部分下功夫,增加帶寬降低延遲,優(yōu)化傳輸速度。
? ?4、開(kāi)源
???這個(gè)“開(kāi)源”是指抓“源頭”,開(kāi)發(fā)網(wǎng)站服務(wù)器自身的潛力,在現(xiàn)有條件不變的情況下盡量挖掘出更多的服務(wù)能力。
??我們應(yīng)該選用高性能的 Web 服務(wù)器,最佳選擇當(dāng)然就是 Nginx/OpenResty 了,盡量不要選擇基于 Java、Python、Ruby 的其他服務(wù)器,它們用來(lái)做后面的業(yè)務(wù)邏輯服務(wù)器更好。利用 Nginx 強(qiáng)大的反向代理能力實(shí)現(xiàn)“動(dòng)靜分離”,動(dòng)態(tài)頁(yè)面交給 Tomcat、Django、Rails,圖片、樣式表等靜態(tài)資源交給 Nginx。
? ? Nginx 或者 OpenResty 自身也有很多配置參數(shù)可以用來(lái)進(jìn)一步調(diào)優(yōu),舉幾個(gè)例子,比如說(shuō)禁用負(fù)載均衡鎖、增大連接池,綁定 CPU 等等。
??對(duì)于 HTTP 協(xié)議一定要啟用長(zhǎng)連接。TCP 和SSL 建立新連接的成本是非常高的,有可能會(huì)占到客戶端總延遲的一半以上。長(zhǎng)連接雖然不能優(yōu)化連接握手,但可以把成本“均攤”到多次請(qǐng)求里,這樣只有第一次請(qǐng)求會(huì)有延遲,之后的請(qǐng)求就不會(huì)有連接延遲,總體的延遲也就降低了。
?另外,在現(xiàn)代操作系統(tǒng)上都已經(jīng)支持 TCP 的新特性“TCP Fast Open”(Win10、iOS9、Linux 4.1),它的效果類似 TLS 的“False Start”,可以在初次握手的時(shí)候就傳輸數(shù)據(jù),也就是 0-RTT,所以我們應(yīng)該盡可能在操作系統(tǒng)和 Nginx 里開(kāi)啟這個(gè)特性,減少外網(wǎng)和內(nèi)網(wǎng)里的握手延遲。
?下面給出一個(gè)簡(jiǎn)短的 Nginx 配置示例,啟用了長(zhǎng)連接等優(yōu)化參數(shù),實(shí)現(xiàn)了動(dòng)靜分離:
server {listen 80 deferred reuseport backlog=4096 fastopen=1024; keepalive_timeout 60;keepalive_requests 10000;location ~* \.(png)$ {root /var/images/png/;}location ~* \.(php)$ {proxy_pass http://php_back_end;}
}
????????5、節(jié)流
????????? “節(jié)流”是指減少客戶端和服務(wù)器之間收發(fā)的數(shù)據(jù)量,在有限的帶寬里傳輸更多的內(nèi)容。
????????? “節(jié)流”最基本的做法就是使用 HTTP 協(xié)議內(nèi)置的“數(shù)據(jù)壓縮”編碼,不僅可以選擇標(biāo)準(zhǔn)的 gzip,還可以積極嘗試新的壓縮算法 br,它有更好的壓縮效果。
?????????不過(guò)在數(shù)據(jù)壓縮的時(shí)候應(yīng)當(dāng)注意選擇適當(dāng)?shù)膲嚎s率,不要追求最高壓縮比,否則會(huì)耗費(fèi)服務(wù)器的計(jì)算資源,增加響應(yīng)時(shí)間,降低服務(wù)能力,反而會(huì)“得不償失”。gzip 和 br 是通用的壓縮算法,對(duì)于 HTTP 協(xié)議傳輸?shù)母鞣N格式數(shù)據(jù),我們還可以有針對(duì)性地采用特殊的壓縮方式
????????HTML/CSS/JS 屬于純文本,就可以采用特殊的“壓縮”,去掉源碼里多余的空格、換行、注釋等元素。這樣“壓縮”之后的文本雖然看起來(lái)很混亂,對(duì)“人類”不友好,但計(jì)算機(jī)仍然能夠毫無(wú)障礙地閱讀,不影響瀏覽器上的運(yùn)行效果。
?????????圖片在 HTTP 傳輸里占有非常高的比例,雖然它本身已經(jīng)被壓縮過(guò)了,不能被 gzip、br 處理,但仍然有優(yōu)化的空間。比如說(shuō),去除圖片里的拍攝時(shí)間、地點(diǎn)、機(jī)型等元數(shù)據(jù),適當(dāng)降低分辨率,縮小尺寸。圖片的格式也很關(guān)鍵,盡量選擇高壓縮率的格式,有損格式應(yīng)該用JPEG,無(wú)損格式應(yīng)該用 Webp 格式。
????????對(duì)于小文本或者小圖片,還有一種叫做“資源合并”(Concatenation)的優(yōu)化方式,就是把許多小資源合并成一個(gè)大資源,用一個(gè)請(qǐng)求全下載到客戶端,然后客戶端再用 JS、CSS切分后使用,好處是節(jié)省了請(qǐng)求次數(shù),但缺點(diǎn)是處理比較麻煩。
????????剛才說(shuō)的幾種數(shù)據(jù)壓縮都是針對(duì)的 HTTP 報(bào)文里的 body,在 HTTP/1 里沒(méi)有辦法可以壓縮header,但我們也可以采取一些手段來(lái)減少 header 的大小,不必要的字段就盡量不發(fā)(例如 Server、X-Powered-By)。
????????網(wǎng)站經(jīng)常會(huì)使用 Cookie 來(lái)記錄用戶的數(shù)據(jù),瀏覽器訪問(wèn)網(wǎng)站時(shí)每次都會(huì)帶上 Cookie,冗余度很高。所以應(yīng)當(dāng)少使用 Cookie,減少 Cookie 記錄的數(shù)據(jù)量,總使用 domain 和path 屬性限定 Cookie 的作用域,盡可能減少 Cookie 的傳輸。如果客戶端是現(xiàn)代瀏覽器,還可以使用 HTML5 里定義的 Web Local Storage,避免使用 Cookie。
?????????壓縮之外,“節(jié)流”還有兩個(gè)優(yōu)化點(diǎn),就是域名和重定向
????????DNS 解析域名會(huì)耗費(fèi)不少的時(shí)間,如果網(wǎng)站擁有多個(gè)域名,那么域名解析獲取 IP 地址就是一個(gè)不小的成本,所以應(yīng)當(dāng)適當(dāng)“收縮”域名,限制在兩三個(gè)左右,減少解析完整域名所需的時(shí)間,讓客戶端盡快從系統(tǒng)緩存里獲取解析結(jié)果。
????????重定向引發(fā)的客戶端延遲也很高,它不僅增加了一次請(qǐng)求往返,還有可能導(dǎo)致新域名的DNS 解析,是 HTTP 前端性能優(yōu)化的“大忌”。除非必要,應(yīng)當(dāng)盡量不使用重定向,或者使用 Web 服務(wù)器的“內(nèi)部重定向”。
????????6、緩存
????????? 緩存不僅是 HTTP,也是任何計(jì)算機(jī)系統(tǒng)性能優(yōu)化的“法寶”,把它和上面的“開(kāi)源”“節(jié)流”搭配起來(lái)應(yīng)用于傳輸鏈路,就能夠讓 HTTP的性能再上一個(gè)臺(tái)階。
????????? 網(wǎng)站系統(tǒng)內(nèi)部:可以使用 Memcache、Redis、Varnish 等專門(mén)的緩存服務(wù),把計(jì)算的中間結(jié)果和資源存儲(chǔ)在內(nèi)存或者硬盤(pán)里,Web 服務(wù)器首先檢查緩存系統(tǒng),如果有數(shù)據(jù)就立即返回給客戶端,省去了訪問(wèn)后臺(tái)服務(wù)的時(shí)間。
????????互聯(lián)網(wǎng)上:緩存更是性能優(yōu)化的重要手段,CDN 的網(wǎng)絡(luò)加速功能就是建立在緩存的基礎(chǔ)之上的,可以這么說(shuō),如果沒(méi)有緩存,那就沒(méi)有 CDN。
????????? 利用好緩存功能的關(guān)鍵是理解它的工作原理,為每個(gè)資源都添加 ETag 和 Last-modified 字段,再用 Cache-Control、Expires 設(shè)置好緩存控制屬性。其中最基本的是 max-age 有效期,標(biāo)記資源可緩存的時(shí)間。對(duì)于圖片、CSS 等靜態(tài)資源可以設(shè)置較長(zhǎng)的時(shí)間,比如一天或者一個(gè)月,對(duì)于動(dòng)態(tài)資源,除非是實(shí)時(shí)性非常高,也可以設(shè)置一個(gè)較短的時(shí)間,比如 1 秒或者 5 秒。這樣一旦資源到達(dá)客戶端,就會(huì)被緩存起來(lái),在有效期內(nèi)都不會(huì)再向服務(wù)器發(fā)送請(qǐng)求,也就是:“沒(méi)有請(qǐng)求的請(qǐng)求,才是最快的請(qǐng)求?!?/p>
????????7、HTTP/2
????????? 在“開(kāi)源”“節(jié)流”和“緩存”這三大策略之外,HTTP 性能優(yōu)化還有一個(gè)選擇,那就是把協(xié)議由 HTTP/1 升級(jí)到 HTTP/2。
????????? HTTP/2 消除了應(yīng)用層的隊(duì)頭阻塞,擁有頭部壓縮、二進(jìn)制幀、多路復(fù)用、流量控制、服務(wù)器推送等許多新特性,大幅度提升了 HTTP 的傳輸效率。實(shí)際上這些特性也是在“開(kāi)源”和“節(jié)流”這兩點(diǎn)上做文章,但因?yàn)檫@些都已經(jīng)內(nèi)置在了協(xié)議內(nèi),所以只要換上 HTTP/2,網(wǎng)站就能夠立刻獲得顯著的性能提升。
?????????一些在 HTTP/1 里的優(yōu)化手段到了 HTTP/2 里會(huì)有“反效果”。對(duì)于 HTTP/2 來(lái)說(shuō),一個(gè)域名使用一個(gè) TCP 連接才能夠獲得最佳性能,如果開(kāi)多個(gè)域名,就會(huì)浪費(fèi)帶寬和服務(wù)器資源,也會(huì)降低 HTTP/2 的效率,所以“域名收縮”在 HTTP/2 里是必須要做的?!百Y源合并”在 HTTP/1 里減少了多次請(qǐng)求的成本,但在 HTTP/2 里因?yàn)橛蓄^部壓縮和多路復(fù)用,傳輸小文件的成本很低,所以合并就失去了意義。而且“資源合并”還有一個(gè)缺點(diǎn),就是降低了緩存的可用性,只要一個(gè)小文件更新,整個(gè)緩存就完全失效,必須重新下載。所以在現(xiàn)在的大帶寬和 CDN 應(yīng)用場(chǎng)景下,應(yīng)當(dāng)盡量少用資源合并(JS、CSS 圖片合并,數(shù)據(jù)內(nèi)嵌),讓資源的粒度盡可能地小,才能更好地發(fā)揮緩存的作用。