作者:Alessandro Ghedini,原文地址:https://blog.cloudflare.com/the-road-to-quic/
QUIC(Quick UDP Internet Connections)是一種新的默認加密Internet傳輸協(xié)議,它提供了許多改進,旨在加速HTTP通信并使其更加安全,其目標是最終取代web上的TCP和TLS。在這篇博文中,我們將概述QUIC的一些關(guān)鍵特性,它們?nèi)绾螢閣eb帶來好處,以及支持這個新協(xié)議所面臨的一些挑戰(zhàn)。
事實上,有兩個協(xié)議同名:“Google QUIC”(簡稱gQUIC),是Google工程師幾年前設計的原始協(xié)議,經(jīng)過多年的試驗,現(xiàn)在已經(jīng)被IETF(互聯(lián)網(wǎng)工程任務組)采用進行標準化。
“IETF-QUIC”(從現(xiàn)在起就是“QUIC”)已經(jīng)與gQUIC有相當大的不同,因此可以將其視為一個單獨的協(xié)議。從數(shù)據(jù)包的格式,到握手(handshake)和HTTP映射,QUIC改進了最初的gQUIC設計,這得益于許多組織和個人的開放協(xié)作,共同的目標是使互聯(lián)網(wǎng)更快、更安全。
那么,QUIC提供了哪些改進?
內(nèi)置安全性(和性能)
QUIC與現(xiàn)在受人尊敬的TCP的一個更根本的差別是,它的設計目標是提供一個默認安全的傳輸協(xié)議。QUIC通過提供安全特性來實現(xiàn)這一點,比如身份驗證和加密,這些特性通常由比傳輸協(xié)議本身更高層的協(xié)議(如TLS)處理。
QUIC初始握手合并了TCP的典型三次握手和TLS 1.3握手,提供了端點的身份驗證和密碼參數(shù)的協(xié)商。對于那些熟悉TLS協(xié)議的人,QUIC用自己的幀格式替換TLS記錄層,同時保持相同的TLS握手消息。
這不僅可以確保連接始終經(jīng)過身份驗證和加密,而且還可以加快初始連接的建立:與TCP和TLS 1.3握手組合起來所需的2次RTT(round-trip)相比,典型的QUIC握手只需在客戶端和服務器之間進行1次RTT。
但QUIC更進一步,它還加密了額外的連接元數(shù)據(jù)(metadata),這些元數(shù)據(jù)可能被中間設備(middle-boxes)濫用來干擾連接。例如,當使用連接遷移(connection migration)時,被動路徑攻擊者可使用數(shù)據(jù)包編號關(guān)聯(lián)多個網(wǎng)絡路徑上的用戶活動(見下文)。通過加密數(shù)據(jù)包編號,QUIC確保除了連接中的端點之外,任何人都不能使用它們來關(guān)聯(lián)用戶的活動。
加密也可以是對僵化(ossification)的一種有效的補救措施,它使得協(xié)議中內(nèi)置的靈活性(例如能夠協(xié)商(negotiate)該協(xié)議的不同版本)。由于實現(xiàn)中的錯誤假設而無法在實踐中使用(僵化(ossification)是導致TLS 1.3長時間無法部署的原因,這只有在幾次修改后才可能實現(xiàn),這些修改旨在防止僵化的中間設備(middle-boxes)錯誤地阻塞TLS協(xié)議的新修訂)。
隊頭阻塞(Head-of-line blocking)
HTTP/2的主要改進之一是能夠?qū)⒉煌腍TTP請求多路復用(multiplexing)到同一個TCP連接上。這允許HTTP/2應用程序并發(fā)地處理請求,并更好地利用可用的網(wǎng)絡帶寬。
與當時的現(xiàn)狀相比,這是一個很大的改進,即如果應用程序希望同時處理多個HTTP/1.1請求,則需要啟動多個TCP+TLS連接(例如,當瀏覽器需要同時獲取CSS和Javascript資源來呈現(xiàn)網(wǎng)頁時)。初始的握手會減慢初始頁面的傳輸速度,這意味著需要多次刷新新頁面。多路復用(Multiplexing)HTTP避免了所有這些問題。
然而,這也有一個缺點:由于多個請求/響應是通過同一個TCP連接傳輸?shù)?,因此它們都會受到?shù)據(jù)包丟失(例如,由于網(wǎng)絡擁塞)的影響,即使丟失的數(shù)據(jù)只涉及單個請求。這被稱為“隊頭阻塞”(head-of-line blocking)。
QUIC更深入一點,為多路復用提供一流的支持,這樣不同的HTTP流可以映射到不同的QUIC傳輸流,但是它們?nèi)匀还蚕硐嗤腝UIC連接,因此不需要額外的握手和共享擁塞狀態(tài)。但是QUIC流是獨立的,這樣在大多數(shù)情況下,一個流的包丟失不會影響其他流。
例如,這可以顯著減少呈現(xiàn)完整網(wǎng)頁(使用CSS、Javascript、圖片和其他類型的資源)所需的時間,尤其是在穿越高度擁擠的網(wǎng)絡、具有高數(shù)據(jù)包丟失率的網(wǎng)絡時。
這么簡單,呃?
為了實現(xiàn)它的承諾,QUIC協(xié)議需要打破許多網(wǎng)絡應用程序認為理所當然的一些假設,這可能會使QUIC的實現(xiàn)和部署更加困難。
QUIC被設計在UDP數(shù)據(jù)報之上傳輸,以便于部署,并避免網(wǎng)絡設備丟棄未知協(xié)議的數(shù)據(jù)包,因為大多數(shù)設備已經(jīng)支持UDP。這也允許QUIC實現(xiàn)在用戶態(tài)(user-space)中生存,因此,例如瀏覽器將能夠?qū)崿F(xiàn)新的協(xié)議特性并將其發(fā)送給用戶,而不必等待操作系統(tǒng)的更新。(而TCP協(xié)議的更新受到網(wǎng)絡設備和操作系統(tǒng)內(nèi)容的約束,這也是QUIC設計在UDP之上的原因之一)
然而,盡管預期的目標是避免破壞,但它也使得防止濫用和正確地將數(shù)據(jù)包路由到正確的端點更具挑戰(zhàn)性。
一個NAT把他們都帶來,在黑暗中捆綁他們(One NAT to bring them all and in the darkness bind them)
典型的NAT路由器可以使用傳統(tǒng)的4元組(源IP地址和端口、目的IP地址和端口)來跟蹤經(jīng)過它們的TCP連接,并通過觀察在網(wǎng)絡上傳輸?shù)腡CP SYN、ACK和FIN包,來檢測新連接何時建立和何時終止。這使它們能夠精確地管理NAT綁定的生命周期(lifetime),內(nèi)部IP地址和端口之間的關(guān)聯(lián),以及外部IP地址和端口之間的關(guān)聯(lián)。
對于QUIC,這還不可能,因為現(xiàn)在廣泛部署的NAT路由器還不了解QUIC,所以它們通常會退回(fallback)到默認和不太精確的處理UDP流,這通常涉及使用任意的,有時非常短的超時,這可能會影響長時間運行的連接。
當NAT重新綁定發(fā)生時(例如由于超時),NAT外圍外部的端點將看到來自不同源端口的數(shù)據(jù)包,而不是最初建立連接時觀察到的源端口,這使得僅使用4元組無法跟蹤連接。
不僅僅是NAT!QUIC打算提供的特性之一稱為“連接遷移”(connection migration),它允許QUIC端點隨意將連接遷移到不同的IP地址和網(wǎng)絡路徑。例如,當已知的WiFi網(wǎng)絡可用時(比如當用戶進入他們最喜歡的咖啡店時),移動客戶端將能夠在蜂窩數(shù)據(jù)網(wǎng)絡和WiFi之間遷移QUIC連接。
QUIC試圖通過引入連接ID(connection ID)的概念來解決這個問題:一個由QUIC包攜帶的可變長度的不透明blob,可以用來標識連接。端點可以使用這個ID來跟蹤它們負責的連接,而不需要檢查4元組(實際上,同一個連接可能有多個ID標識,例如,在使用連接遷移(connection migration)時,為了避免鏈接不同的路徑,但是這種行為由端點控制而不是中間設備控制)。
然而,這也給使用選播(anycast)尋址和ECMP路由的網(wǎng)絡運營商帶來了一個問題,在這些網(wǎng)絡運營商中,一個目的地IP地址可能識別數(shù)百甚至數(shù)千個服務器。由于這些網(wǎng)絡使用的邊緣路由器還不知道如何處理QUIC流量,因此可能會發(fā)生屬于同一QUIC連接(即具有相同連接ID)但具有不同4元組(由于NAT重新綁定或連接遷移)的UDP數(shù)據(jù)包可能最終被路由到不同的服務器,從而中斷了連接。
為了解決這一問題,網(wǎng)絡運營商可能需要采用更智能的第4層負載平衡解決方案,這種解決方案可以在軟件中實現(xiàn),并且無需接觸邊緣路由器即可部署(例如,F(xiàn)acebook的Katran項目)。
QPACK
HTTP/2引入的另一個好處是頭部壓縮(或HPACK),它允許HTTP/2端點通過刪除HTTP請求和響應中的冗余來減少通過網(wǎng)絡傳輸?shù)臄?shù)據(jù)量。
特別是,在其他技術(shù)中,HPACK使用動態(tài)表填充了從以前的HTTP請求(或響應)發(fā)送(或接收)的頭,允許端點在新的請求(或響應)中引用以前遇到的頭,而不必重新傳輸它們。
HPACK的動態(tài)表需要在編碼器encoder(發(fā)送HTTP請求或響應的一方)和解碼器decoder(接收它們的一方)之間同步,否則解碼器decoder將無法解碼它接收到的內(nèi)容。
對于TCP之上的HTTP/2,這種同步是透明的,因為傳輸層(TCP)負責以相同順序發(fā)送HTTP請求和響應,更新表的指令可以由編碼器encoder作為請求(或響應)本身的一部分發(fā)送,從而使編碼非常簡單。但對QUIC來說,這更復雜。
QUIC可以在不同的流上獨立地傳遞多個HTTP請求(或響應),這意味著,盡管它負責按單流的順序傳遞數(shù)據(jù),但在多個流之間不能保證排序。
例如,如果客戶端通過QUIC stream A發(fā)送HTTP請求A,而通過stream B發(fā)送請求B,則由于網(wǎng)絡中的數(shù)據(jù)包重新排序或丟失,服務器可能會在請求A之前接收到請求B,并且如果請求B被用來自請求A的報頭編碼,服務器將無法解碼,因為它還沒有看到請求A。
在gQUIC協(xié)議中,這個問題只需在同一個gQUIC流上序列化所有HTTP請求和響應頭(而不是主體),這意味著無論發(fā)生什么,消息頭都將按順序傳遞。這是一個非常簡單的方案,它允許實現(xiàn)(implementations)重用大量現(xiàn)有的HTTP/2代碼,但另一方面,它增加了QUIC原本設計減少的隊頭阻塞(head-of-line blocking)。IETF QUIC工作組因此設計了HTTP和QUIC之間的新映射(“HTTP/QUIC”),以及一種稱為“QPACK”的頭壓縮新方案。
在HTTP/QUIC映射和QPACK規(guī)范的最新草案中,每個HTTP請求/響應交換都使用自己的雙向QUIC流,因此沒有隊頭阻塞(head-of-line blocking)。另外,為了支持QPACK,每個peer方創(chuàng)建兩個e額外的單向QUIC流,一個用于向另一個peer方發(fā)送QPACK表更新,另一個用于確認另一方接收到的更新。這樣,QPACK編碼器encoder只有在解碼器decoder顯式地確認了動態(tài)表引用之后才能使用它。
偏轉(zhuǎn)反射(Deflecting Reflection)
基于UDP的協(xié)議中的一個常見問題是它們?nèi)菀资艿椒瓷涔簦╮eflection attacks),即攻擊者通過欺騙數(shù)據(jù)包的源IP地址,使其看起來像是來自受害者,從而欺騙原本無辜的服務器向第三方受害者發(fā)送大量數(shù)據(jù)。
當服務器發(fā)送的響應恰好大于它接收到的請求時,這種攻擊非常有效,在這種情況下,我們稱之為“放大”。
TCP通常不用于此類攻擊,因為在其握手(SYN,SYN+ACK,…)過程中傳輸?shù)某跏紨?shù)據(jù)包具有相同的長度,因此它們不會提供任何潛在放大攻擊的風險。
另一方面,QUIC的握手是非常不對稱的:就像TLS一樣,在它的第一次傳輸中,QUIC服務器通常發(fā)送自己的證書鏈(certificate chain),它可能非常大,而客戶端只需發(fā)送幾個字節(jié)(TLS ClientHello消息嵌入QUIC包中)。因此,客戶端發(fā)送的初始QUIC包必須填充到特定的最小長度(即使包的實際內(nèi)容要小得多)。然而,這種緩解仍然不夠,因為典型的服務器響應跨越多個數(shù)據(jù)包,因此仍然可以遠遠大于填充的客戶端數(shù)據(jù)包。
QUIC協(xié)議還定義了一個顯式的源地址驗證(source-address verification)機制,在該機制中,服務器不發(fā)送長響應,只發(fā)送一個很小的“重試(retry)”包,其中包含一個唯一的加密令牌,然后客戶端必須在新的初始包中向服務器回顯。這樣,服務器就有了更高的信心,即客戶機不會欺騙自己的源IP地址(因為它收到了重試數(shù)據(jù)包(retry packet)),并且可以完成握手。這種緩解的缺點是,它將初始握手持續(xù)時間從1次RTT增加到2次。
另一種解決方案包括減少服務器對反射攻擊(reflection attack)變得不那么有效的響應,例如使用ECDSA證書(通常比RSA證書小得多)。我們還一直在嘗試使用現(xiàn)成的壓縮算法(如zlib和brotli)壓縮TLS證書的機制,這是gQUIC最初引入的一個特性,但目前在TLS中還沒有。
UDP性能
QUIC經(jīng)常出現(xiàn)的一個問題是現(xiàn)在廣泛部署的硬件和軟件無法理解它。我們已經(jīng)在另一個C端的路由器上測試了數(shù)據(jù)的傳輸性能。我們已經(jīng)了解了QUIC如何嘗試處理像路由器這樣的網(wǎng)絡中間設備,但是另一個潛在的問題是在QUIC端點上通過UDP發(fā)送和接收數(shù)據(jù)的性能。多年來,人們做了大量的工作來盡可能地優(yōu)化TCP的實現(xiàn),包括在軟件(如操作系統(tǒng))和硬件(如網(wǎng)絡接口)中構(gòu)建卸載功能,但目前還沒有一種方法可用于UDP。
然而,等QUIC實現(xiàn)時也能利用這些功能,這只是時間問題。例如,最近在Linux上實現(xiàn)UDP通用分段卸載的工作,它允許應用程序以單個UDP段(或足夠接近)的代價在用戶態(tài)(user-space)和內(nèi)核態(tài)(kernel-space)網(wǎng)絡堆棧之間捆綁和傳輸多個UDP段,除了在Linux上添加零拷貝套接字支持之外,還允許應用程序避免將用戶態(tài)(user-space)內(nèi)存復制到內(nèi)核空間的成本。
結(jié)論
與HTTP/2和TLS 1.3一樣,QUIC將提供許多新特性,這些特性旨在提高web站點的性能和安全性,以及其他基于Internet的資產(chǎn)。IETF工作組目前正準備在今年年底發(fā)布QUIC規(guī)范的第一版,Cloudflare的工程師們已經(jīng)在努力工作,為我們所有的客戶提供QUIC的好處。