High Performance Browser Networking - TCP UDP TLS

時延

時延的定義和標準

時延簡單的說是從原點到目標點傳送一條信息或者一個數據包,所花費的時間。 時延=發送時延+傳播時延+處理時延+排隊時延:

Propagation delay 傳播時延

傳播時延這個概念,是指電磁信號或者光信號在傳輸介質中傳輸的時延,而在光纖或者銅線中,光信號和電磁信號的傳播速度都在20萬公里/秒以上,在傳輸介質中傳輸的是電磁信號或者光信號,而不是數據!

Transmission delay 傳送時延
發送時延是指結點在發送數據時使數據塊從結點進入到傳輸媒體所需的時間,也就是從數據塊的第一個比特開始發送算起,到最後一個比特發送完畢所需的時間。發送時延又稱爲傳輸時延

Processing delay 處理時延
處理數據包的頭部,校對位錯誤, 並確定數據包要傳輸的方向

Queuing delay 排隊時延
數據包等待處理的時間

總的時延是從客戶端到服務器所有時延的總各,Propagation 時間是由兩者之間的距離和傳播的介質(光纖或銅線)決定。另一方面,Transmission delay 是由兩者建立起來傳輸鏈接的傳輸速率決定的,跟兩者之者的距離沒有關係。 舉例來說, 假設我們分別在1Mb的鏈接和100Mb的鏈接,傳遞一個10Mb的文件。 前者的時間爲10秒,而100Mbps時,只需花費0.1秒。

下一步, 當數據包到達一個路由器, 路由器必需檢測數據包的頭,從而決定下一個路由, 同時還需要對數據進行校驗, 所有的這些邏輯都是在路由器硬件上完成,所以Processing delay是非常小的, 但它也確時存在。 最後來講講Queuing delay. 當數據包從較快的傳速速率(光釺)到一個100MB的路由器,這就常過了路由器的處理速度, 那麼這個數據包就需要進入到緩存區提排隊。 這個排隊時間,就是Queuing Delay.

光速與Propagation delay

光速是299, 792, 458米每秒, 但光速在介質中傳播會有能量損耗, 這只是一個理論的值,通過以下圖表,可以清楚的瞭解光纖實際的速度和造成的Propagation 延時

路徑 距離 真空光速延時 光纖延時 光纖信號往返時間
New York to San Francisco 4,148 km 14 ms 21 ms 42 ms
New York to London 5,585 km 19 ms 28 ms 56 ms
New York to Sydney(悉尼) 15,993 km 53 ms 80 ms 160 ms
繞赤道一週 40,075 km 133.7 ms 200 ms 200 ms

光纖上的傳輸速度是挺快的, 但從紐約到悉尼還是花費了160毫秒.  這也僅僅是理論上的值(兩個城市是直接通過光纖相連),  實際上數據包會經過不同的路徑(悉尼到舊金山, 在到紐約等等)。經理的路徑越多,就會引入更多的處理延時,等待延時, 傳輸延時。最終RTT(Round-trip time)將達到 200 - 300ms.

通常100-200ms是正常的值, 如果超過了300ms, 那麼整個交互就變慢了.  可以使用Content delivery network(CDN),  將服務器的資料緩存在離客戶端最近的服務器。 減少兩者之者的距離.

構建TCP

互聯網由兩個重要的協議組成,IP 和 TCP.   IP(Internet Protocol) 提供了通信雙方之間的路由以及尋址,它只管發送數據,而不保證數據是否完速的傳送到了接收端。TCP(Transmission Control Protocal), 提供了在不可靠的通道上,建立一個抽像的可靠網絡。

TCP 提供了一個可靠的抽像網絡。它嚮應用程序隱藏了複雜的網絡通信細節: 重發丟失的數據, 按序發送,擁塞控制, 數據的完整性等。 當你使用TCP時,你可以保證發送和接受到的數據是一樣的,而且順序也是一樣的。 所以TCP是保證準確傳輸最佳化的協議。

TCP 並不是 HTTP唯一的傳送協議, HTTP也可以建立在用戶數據報協議(User Datagram Protocal) 或者 UDP,  也可以選擇其它傳送協議。 但在實際中, HTTP在互聯網的通信都是通過 TCP。

所以理解TCP的核心運行原理,是我們優化web性能的基礎知識。 

三次握手

所有的TCP都是從三次握手開始, 在客戶端或者服務器交換任意應用數據之前,它們必須先建立連接,確認數據包的序列號, 以及其它用於連接的特殊變量。 由於安全原因,序列號是從雙方中隨機挑選。

SYN

客戶端會挑選出一個數字序列號 X, 並且發送一個SYN 數據包(包含額外的TCP 標記和選項)

SYN ACK

服務器接受到 syn 包, 在序列號X上加1. 並且挑選一個序列號Y,  並附上它自己的標記和選項, 並響應這個數據包

ACK

客戶端對x, y 加1 , 併發回ACK包, 完成握手。

圖1-1

當三次握手完成, 應用層數據就可以在客戶端和服務器之間傳送。 客戶端可以在ACK 包發送完後立即傳送數據包。 但服務器必須在接受到ACK包之後,纔可以。 整個的啓動過程(三次握手) , 每個TCP連接都需要進行, 所以對於使用TCP的應用程序, 這個過程會非常重要: 每一個新的連接在數據傳輸之前都會有一個響應延時。

舉一個例子, 如果我們的客戶端在New York, 但服務器是在London,  TCP連接是建立在光纖上的, 三次握手將至少花費56 毫秒(客戶端可以在第三次ACK後,立即傳輸數據): 28ms 是一個方向的Propogation delay.

三次握手產生的延時,使得建立新的TCP連接是昂貴的, 這也是爲什麼在使用TCP時,需要對已有連接進行重要最大的原因之一.


Slow-Start 慢啓動

你可以通過 http://www.cnblogs.com/fll/archive/2008/06/02/1212479.html 瞭解TCP 對端到端數的可靠性, 以及流量控制, 網絡擁塞。 在來看這一段文章。

TCP的提出解決了流量控制的問題(通過rwind, 告知客戶端,我能處理的數據量). 但是網絡擁塞的問題出現在了。 流量控制防止了發送端(sender) 發送數據的速度超過接收者(receiver)的處理速度。 但TCP還沒有考慮到任何一端的網絡帶寬是否被全部佔用(發生擁塞)。 所以需要有一種機制來保證兩端之者的有一個合理的傳輸速度.

我們看一個例子, 假設你在家裏看一個非常大的視頻(在線) , 爲了保證你有最好的體驗,整個帶寬都會被視頻佔用。 這時你的朋友用你家的網絡下載一個軟件。 但這裏你家的網絡帶寬已經非常少了,所以 video 服務器必須調速它的數據傳送率。 否則,如果它還是以相同的速度傳送的話,數據只會只簡單的堆積在某個中間網關,並且數據包將會被丟棄。導致網絡的低效利用。

1988年, Van Jacobson 和 Michael J.Karels 提出了幾個算法,來解決這些問題: slow-start(慢起動), congestion avoidance(擁塞避免), Fast retransimit(快速重傳),  Fast recovery(快速恢復)。 很快這四個算法成爲了TCP規範中的強制部分。 

爲了理解slow-start 在實際應用中造成的影響,我們回到之前的例子, 我們的客戶位於New York.  嘗試從London的服務器獲取一個文件。 首先經歷三次握手, 在此其間,兩方都通過ACK包,向對方告知自己的 receive window(rwnd)的大小。  一旦後一個ACK包進入網線,我們就可以交換應用數據。

客戶端和服務器之間交換的數據大小是多少才合適呢? 這恰恰就是slow-start 被設計出來的原因了。 一開始, 服務器初始化每一個TCP連接 初始化一個 congestion window(cwnd 擁塞窗口), 並且保守的設置一個初始值 (由Linux系統指定大小).

Congestion window size(cwnd)  
在接收到另一端返回 的acknowlledgment(ACK)之前, 發送端可以發送最大的數據。 最初, cwnd的開始值爲 1個MSS(最大報文段,  可以通過 http://blog.csdn.net/wilsonpeng3/article/details/12869233瞭解TCP報文頭 ), 在1999年, R2581增加一以了4個MSS. 到2013年已經增加到10MSS。

cwnd 變量不會在發送端和接收端之間告知對方和交換。 所以在這種情況下,它將是一個私有變量,在兩個發送端維護。 客戶端和服務器都是在三次握手後,發送第一個數據包時, 初始化1MSS.

比如瀏覽器(圖1-1的第二步, y+1, x+1 後就可以發送數據了, 那麼此時瀏覽器的cwnd 爲 1MSS, 在一個RTT時間內,收到帶ACKed的數據包後,會增加1個MSS. 可以發送兩個數據包了。)

而服務器要在接到收到瀏覽器的ACK包後,纔可以發送數據, 它的cwnd 爲1MSS, 在下一次接收到客戶端的ACKed包後,會增加1MSS.




那我們slow-start對我們的瀏覽器應用程序有什麼影響呢? HTTP 和很多其它應用程序都是運行在 TCP協議之上。  不管帶完是多少, 每一個TCP邊接必須經歷 slow-start。 我們不能直接將合適的流量應用到每個連接。

我們都是以最小的 cwnd 開始, 在每次  RTT之後, 指數增加cwnd的大小.

方程式 2-1 表式的是, 達到指定的傳輸速度所需要的時間:


讓我們假設以下情下:
  • 客戶端和服務器的速度達到 65,535 (64KB)
  • 初始cwnd: 4 MSS (RFC2581)
  • RTT(一個響應用的時間): 56ms (London to New York)

每一個新建的TCP連接的吞吐量都被限制爲 cwnd的大小, 實際上, 要達到64 KB的限制, 我們將增加cwnd的大小爲45 MSS,  這需要花費244毫秒:


如圖2-4所示, 這裏4次請求與響應, 要經歷好幾百的延遲,纔可以達到客戶端與服務器的吞吐量, 64KB.  而在實際,服務器與客戶端之前的速度以Mbps爲單位, 在慢啓動之前,這些都是速度都是無效的。



圖 2-4

爲了減小在增加擁塞空口大小,所花的時間,我們可以減小 服務器與客戶端 RTT的時間。 例如, 將服務器佈置到離客戶端更進的位置。 或者,我們可以增加初始的 cwnd的大小 到 10 MSS (RFC 6928).

對於視頻以及大型文件來說, Slow-start不是特別大的問題, 但對於很多短的和突發的http來說, 因爲它限制了帶寬吞吐量的能力(高帶寬,但使用不上).  這就會對性能造成影響。


注: slow-start restart 對於空閒的連接(比如http keepalive connections, 不傳送數據時), 進行重置到安全 cwnd 值。 這個機制對於 http 長連接造成性能影響,可以通過以下命念關閉
  • $> sysctl net.ipv4.tcp_slow_start_after_idle
  • $> sysctl -w net.ipv4.tcp_slow_start_after_idle=0

爲了瞭解三次握手和慢啓動對http請求造成的影響,我們假設  New York的客戶 從 London的服務器請求一個20  KB的文件。新的TCP連接,如圖 2-5所示。

  • RTT 的時間爲 56ms
  • Client 和 Sever 之間的帶寬 5 Mbps;
  • Client 和 server 的 receive window(rwnd): 65,535 bytes (64KB)
  • 初始 cwnd : 4 MSS (4 × 1460 bytes ≈ 5.7 KB)
  • 服務器處理響應的時間: 40 ms
  • GET請求大小,小於單個 segement( 1460)

圖2-5

0 ms  Client 通過SYN包,進行TCP第一次握手

28ms  服務器返回一個SYN-ACK, 並且有指定 rwnd 大小

56ms  Client  發送一個對SYN-ACK所的確認包, 並指定 自已的 rwnd大小, 並立即發送一個 HTTP GET  請求

84ms  服務器接受 HTTP  request, 並花 40 ms處理

124ms  服務器完成,併產生一個20kb的響應, 並且發送4 TCP segments的數據(5480 bytes), 並暫停,等待客戶端返回一個 ACK包

152ms  客戶端接收到 4 個片段的數據, 並確認每一段數據, 並返回一個ACKed 包。

180ms 服務器增加cwnd的值, 併發送8 個數據段

208ms  客戶端接收到 8個數據段,並確認每一段數據

236ms  服務器增加每個ACK 包的 cwnd,  併發送剩餘的數據段

264ms  客戶端接收剩餘數據段,並返回每一個段的ACKs包

在一個新的TCP傳遞一個20KB的數據,  花費了264ms . 讓我們對比一下,重用相同的TCP連接,  並請求相同的內容


圖2-6

0 ms 客戶端發送請求

28ms 服務器接收到請求

68ms 服務器處理完請求,並生成20KB的響應,但是當前的cwnd爲15 segments,  因此可以一次性發送完20KB的數據

96ms 客戶端接收到15個數據段,並ACKs  它們


同樣相同的請求在相同的連接上(除了三次握手)。  性能上提升了275%;

在這兩個例子中, 5 Mbps的帶寬沒有對性能產生任何影響,主要的影響因素是擁塞窗口大小。 


擁塞避免


要認識到一點, TCP是通過數據包丟失返饋機制來調節 網絡性能。 從慢啓動可以看到,cwnd可以很快的增長上來(2的N次方),從而最大程度利用網絡帶寬資源,但是cwnd不能一直這樣無限增長下去,一定需要某個限制。TCP使用了一個叫慢啓動門限(ssthresh)的變量,當cwnd超過該值後,慢啓動過程結束,進入擁塞避免階段, 或者發生了丟包現像,也會進入到擁塞避免階段.  (http://www.cnblogs.com/fll/archive/2008/06/10/1217013.html)

如果發現丟包現像, 擁塞避免算法,認爲網絡發生了擁塞:  在網絡中的某處,我們遇到了擁塞的線路或者路由器, 它們強制性的丟棄了數據包。 在此時, 我們需要調速 擁塞窗口的大小, 以免丟失更多的包, 同時避免擠佔網絡。

一旦擁塞窗口被重置, 擁塞避免會採用它自己的算法增加窗口的大小, 保證包的最小丟失。 如果你曾經觀察過TCP連接的,就知道圖表的線爲什麼是鋸齒形的。 TCP 通過擁塞控制和擁塞避免,調整擁塞窗口的大小, 以免在網絡中發生丟包。


Bandwidth-Delay Product (帶寬遲延乘積)

TCP 內製的擁塞控制和擁塞避免帶來了另一個重要的性能優化: 發生者和接收者最佳值,這取決於RTT  和 兩者之前的帶寬( data rate)

我們回憶一下之前內容, 在發送者和接收者之間傳送中的數據大小(unacknowledged),取決於 rwnd 和 cwnd 中最小的一個。 rwnd 的大小每次都會出現在ACK包中(固定), 而 cwnd 依據發送者的擁塞控制和擁塞避免,動態調整的。 

如果發生者 或 接收者 超過了最大的unacknowledged data(未確認數據), 它必須停下來,等待其它之前發送過的包的確認信息(ACK).  那麼它需要等待多久呢? 這取決於兩者之者的響應時間RTT.

Bandwidth-Delay Product

數據鏈路上帶寬與 end-to-end 之間的延時乘積。 它所表示的是, 任一時間點, 可以發送的最大 未確認(unacknowledged)的數據.

sender 或 receiver 在停下來等待之前發送的包確認信息, 就會有一段時間空隙(data gap), 在這段空隙內,發送端是不能在發送數據的。 爲了解決這個問題, 窗口的大小就需要調整到足夠大, 這樣服sender在接受到receiver 返回的ACK包之前,也可以繼續發送數據。 這樣就沒有gaps。 因此最優的窗口大小依賴於 Round-trip time (RTT).  當窗口比較小的時候, 你將會限制連接的吞吐量。 


圖2-7 Figure 2-7. Transmission gaps due to low congestion window size

那麼流量控制窗口(rwnd) 和 擁塞控窗口(cwnd) 需要多在布呂尼 ? 計算這個值非常簡單, 首先, 讓我們假設cwnd 和 rwnd中,最小的值爲 16KB.  RTT時間爲 100ms:

不管在sender 和 receiver的有效帶寬是多大, 這個TCP 連接不會超過 1.31 Mbps; 爲了達到更大的輸出, 我們需要增加 最小窗口值 或者減少兩者之間的RTT.
同樣,我們也可以通過 RTT 和 兩者之者的有效帶寬, 計算出最佳的 window 大小, 讓我們假設RTT 同樣爲 100 ms. 但發送者有10 Mbps的有效帶寬, 並且接收者的帶寬高於100Mbps.  假設兩者之者沒有網絡擁塞, 那我們發送到客戶端的速度爲 10Mbps;

窗口大小最少需要122.1 KB 才能適應 10 Mbps 的數據鏈路。 但rwnd最大的值爲64KB, 除非通過其它方法調整 - 查看 "Window Scaling (RFC 1323" on Page 18"


Head-of-Line Blocking  線頭阻塞

TCP 是在不可靠的信道上提供了一條抽像的可靠網絡。 這包括基礎包的錯誤檢察和修改, 按序發送, 重發丟失的包, 以及流量控制, 擁塞控制 和擁塞避免(保證網絡資源的最有效利用).  所以很多應用都是使用TCP.

但TCP不是唯一可以選擇的, 有時TCP不是最好的選擇。 特別是TCP有一些特性會引入不必要的延遲和性能問題, 比如有序 和 可靠的數據包傳遞(有些應用並不總是需要這些特性).

爲了更好的理解這個問題, 我們回想一下,每個TCP 包 會攜帶一個唯一的序列號(Number), 並且數據必須有序的傳遞到接收者那。  如圖2-8所示. 如果一個包在路由中丟失了, 則所有的後序的包(2,3)都必須在接收者的 TCP 緩存裏等待 丟失包(1)的重傳。由於這個工作是在TCP層, 我們的應用程序不能看到TCP重傳或者緩存中的包隊列, 應用程序在可以訪問到這些數據之前,必須等待完整的包序列。 可以簡單點說, 應用程序嘗試從socket讀取數據會有遲時發生。 這就是 TCP head-of-line(HOL) blocking(線頭阻塞)

線頭阻塞使我們的應用程序避免了對數據包進行重新排序, 使得應用程序代碼容易編寫。 可是, 這會引入不確定的延時,以等待丟失的數據包重發。 這種延時可以稱爲 (jjitter, http://blog.csdn.net/junllee/article/details/6110912) ,這影響到程序的性能。


圖2-8
更進一步的說, 有的應用程序甚至不需要可靠的遞送,或者有棄的遞送: 比如數據包是一個獨立的消息, 則有序遞送就完全沒有必要, 又比如,每一條消息都會覆蓋之前的傳送的消息, 那麼可靠傳送也沒有必要,丟失了可以在之後中獲得。 但不幸的是TCP 不提供這些配置 - 所有的數據包都是以可靠而有序的方式傳遞數據.

應用程序運行以無序或者忽略丟包的的方式運行,但必須使用其它的傳輸協議, 這就是UDP.

Packet Loss is OK (丟包的發生也是有好處的)
實際上, 數據包的丟失會使TCP獲得更好的性能。 丟失的包作爲一種反饋機制,TCP知道網絡擁塞, 就會調整接收者和發送者之間的發送速率。 避免造成整個網絡癱瘓。 而且, 有的應用程序可以忍受數據包丟失: audio, video , 遊戲狀態更新。  它們都不需要可靠和有序的數據傳遞。 順便說一樣, 這也是什麼WebRTC(網頁實時通信()是基於UDP作爲傳輸協議的原因.
如果一個包丟失, 視頻解碼器會簡單的在視頻中插入一小段空白, 並繼續處理之後的處據包, 如果這個空隙非常小,視頻解碼都不會通知使用者,這樣就不用在視頻輸出的時候, 以暫停的方式等待丟失的包。
同樣的,如果我們正在傳遞的數據是 3D遊戲中一個角色的狀態更新, 這時我們等待的數據是用來描述 T-1 時間點的狀態, 而T時間點的數據包已經接收,那麼T-1 秒的數據包就是不需要的了。 理想的是,我們可以接收到每一個狀態更新, 但是爲了保證遊戲的不發生延遲。 我們可以接愛間歇性的丟包, 以保證較低的延遲.


TCP 優化

TCP 協議是一種自適應的協議,以保證公平的對待網絡的各節點,並最有效的利用各種網絡。(公平, 有效利用)。 因此, 優化TCP最好的方法是讓TCP知道當前的網絡條件, 並且依據上次和下層的協議類型和要求,調整TCP的行爲。 比如, 無線網絡,它需要不同的擁塞算法; 有的應用程序爲了達到最好的體驗,會自定義QoS語義。

不同的應用程序有不同的要求, 而每一個TCP算法,也有很多影響因子,這些都導致TCP的優化,成爲了學術和商業研究中永恆的課題。 在目前爲前,我們只是簡單的介紹了影響到TCP 性能的一些因素。  當然還有一些其它的因素,比如 selctive acknowledgments(SACK 選擇性確認), delayed acknowledgments (遲延性確認),  快速重發等等。 這使得每個TCP會話都變得複雜, 難以理解, 分析, 調整。
雖然每一個TCP算法都有自已特定的細節,並繼續發展出不同的反饋(feedback)機制,但核心的原理依然相同, 這些原理導致的性能問題也沒有改變:
  • TCP  三次握手 引入的延遲  2 RTT
  • TCP slow-start 都會發生在每個新的TCP連接
  • TCP 流量 和 擁塞控制, 影響到所有TCP連接的吞吐量(帶寬)
  • TCP 吞吐量會受到cwnd(擁塞窗口)大小的影響

因此, 每個tcp 連接在現代化的高速網絡下,傳送數據的速率也是受限於 發送者和接受者之間的 roundtrip 時間的影響(可以看看帶寬遲延積)。或者這麼解釋, 雖然可以無限制的增加帶寬, 採用光速傳送數據,從New York 到 London的遲延還是有28ms.  在很多情況下,TCP的瓶頸不是帶寬的大小,而是延遲的大小。 可以查看圖2-5.

調優服務器配置

首先需要調優TCP 會使用到的每一個 buffer(rwnd, cwnd)的值 和超時變量,這裏的影響TCP的因素非常多, 所以最簡單有效的方法是 升級服務器到最新的系統。 TCP 最佳的實踐和算法都存在於最新的系統內核中。

有了最新的系統內核, 就能保存你的服務器配置使用以下最佳實踐:
  • 提升TCP's 的初始化擁塞窗口的大小  原書 26頁:  cwnd 在一開始如果有一個很大的值, 它允許TCP 在第一次RTT內傳遞更多的數據。 同時意味着它可以加速window的增長。 特別是 在優化突發性,短連接中起來決定性的作用。
  • Slow-Start Restart 原書23頁 : 禁用在TCP閒置時,進行慢啓動。 這將明顯的做優化長連接TCP的性能。
  • Window Scaling  增加 接收窗口的大小  原書18 : 增加 rwnd 大小,達到網絡最好的吞吐量
  • TCP 快速打開 原書 16: 在某些情況下, 允許應用程序數據可以在初始的SYN包,一起發送。 TFO是一種新的優化方式, 要求在客戶端和服務器都支持。 
除了這些, 根據你的應用程序, 你也可以調整服務器其它的TCP設置。以優化高速連接, 內存使用 或者其它。 但這些配置會依賴於系統平臺,應用程序, 以及硬件 -- 在此之前,你必須查閱系統文檔。

調優應用程序行爲

  • 儘量減小要發送數據的大小
  • 我們不能改變數據發送大小的速度, 但是可以將數據放到離客戶端更近的地方。
  • TCP 連接的重用,對性能的改善很明顯
減小不必要的數據量傳輸,是最佳的一個優化。  e.g : 減小不必要的資源 或者 通過壓縮算法將數據壓縮後傳輸。 之後, 將數據放到離客戶最近的地方, 比如使用CDN,  它會減小網絡RTT 帶來的延遲, 改善TCP性能。  最後 , 在可能的情況下,重用TCP連接

性能檢測清單

以下是一個簡短的優化清單,可以用在平常的工作中:
  •  升級你的系統內核到最新的版本
  • 確保cwnd 的大小設置爲了 10 MSS
  • 禁用 slow-start after idle
  • 允許窗口可伸縮
  • 消除多餘數據的傳輸
  • 壓縮傳送的數據
  • 將服務器部署在離客戶最近的地方,減小RTT
  • 在可能的情況下, 重用TCP

UDP

udp 對前端沒有什麼影響,它是一種不可靠的協議, 可以通過google 瞭解udp的特性,以及NAT.  最新的採用的技術是Google 在瀏覽器提供的WebRTC. 

TLS

SSL 協議最初是由Netscape公司開發的, 保證web中的電子商務的安全性。 它通過加密技術保護客戶的私有數據, 以及安全的傳輸認證信息和信息的完整性。 爲了達到這幾個目標, SSL協議是在應用層實現, TCP的上一層就是SSL層, 之後允許其它的協議運行在它上面,比如HTTP, email, 即時通信等等。

當使用了SSL 時, 第三方的只能推斷出連接的雙方是誰, 加密協議是什麼, 以及數據傳送的頻率和大概的數據量, 但不能查看和修改任何實際的數據。


圖 4-1
SSL 2.0 是第一個發佈的版本, 但很快被SSL3.0取代, 因爲2.0被髮生有安全漏洞, SSL一開始是屬於網景公司, 經過IETF的努力,成爲了RFC2246 中的一個標準協議。 這就是TLS1.0, 它實際上是SSL 3.0的一個升級。

加密、 認證、 完整性

TLS 會爲所有運行在它上面的應用程序提供三方面的基礎服務: 加密, 認識, 以及數據的完整性, 從技術上來說,你可以根據自己的需要,使用一種或者兩種,比如你可以使用未被驗證的安全證書, 但你知道這會導致安全問題, 在實踐中, 一個安全web應用將會使用到三種服務。

加密 Encryption
提供一種機制,使數據以密文的形式發送
Authentication
提供一種機制,數據發送者的身份, 而不是第三方發送的數據
完整性
提供一種機制,保證數據不會被篡改和僞造

爲了建立一個安全的密碼數據通道, 連接的兩端必須協商好,使用哪種密碼套件來加密數據。 TLS 協議規範中定義了握手協議,用來處理處據交換, 我們將在 "TLS Handshake"  原書 50頁 有詳細解說。 握手協議使用對方的公共密鑰加密數據(非對稱加密), 數據接收方自己的私鑰進行解密, 這樣就可以在非安全信道上共享公共密鑰。

握手協議也允許連接的雙方,認證對方的身份。 當使用者在瀏覽器端, 認證機制充許客戶校驗自稱的服務器是誰(比如你的銀行), 而不是簡單冒充的目的名稱和IP 地址。 這種認證是基於建立的信任鏈(可以通過 信任鏈和認證中心,即有一個權威的頒證機構,幫你確認,這個證書所對應的銀行)。 另外, 服務器也可以校驗客戶的身份, 比如, 一個公司代理服務器可以識別所有的員工, 每一個員工都有它們自己的一個唯一證書(不是CA發的,而是公司自己通過軟件生成).

現在TLS 有了加密和身份認證, TLS協議也提供了它,自己擁有的消息分幀機制, 並通過 message authentication code(MAC) 對每一條消息進行簽名。 MAC算法是一種單向加密散列函數, 它的key 是通信雙方協商 的一個結果(session key) ,  可以查看http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html 。 每當TLS record  要發送, 就會產生一個MAC值, 並附加在消息的後面。這樣接收者,就可以計算和校驗發送過的值, 以確保消息的完整性和身份認證。 (簡單點說,密文通過MAC算法,基於session key 生成密文的校驗值, 並把這個校驗值一起發送到接收端,接收端使用MAC算法和相同的session key 對接到的信息生成校驗值,並對比是否一樣,就能保證消息的一致性)

綜述, 加密,認證,數據的完整性保證了Web的安全性, 所有的現代瀏覽器都支持多種密碼套件, 可以用來認證客戶端和服務器身份,並且透明的完成信息的完整性校驗和每一個record的校驗。

TLS 握手

在客戶端和服務器開始效換應用數據之前, 兩者必須協調一個加密通道: 客戶端和服務器協商好雙方使用的TLS協議版本, 加密的方法(比如RSA公鑰加密),如果有需要,還要校對證書。 但每一步都會有新的數據包往返於客戶端和服務器, 所有的TLS連接在啓動的時候都有這樣的延遲發生。


圖 4-2 TLS握手協議

0 ms TLS 運行在TCP連接上, 所以我們必須先完成TCP的三次握手, 將花費一次往返的時間 56 ms

56ms 此處TCP可以使用了, 客戶端以文本的形式,發送多個參數。 比如客戶端需要運行的TLS協議版本, 它支持的加密算法, 和其它TLS  會用到的選項

84ms  服務器選擇TLS 版本(依據客戶端要求), 並依據客戶端支持的加密算法,確定加密套件。 並附上自己的證書, 發回一個響應給客戶端。 在這裏,還有一些可選項, 服務器也可以要求客戶商提供證書, 以及擴展TLS的參數

112ms 假設兩端可以使用協商的TLS 版本和密碼套件, 並且客戶端對服務器提供的證書是信任的, 客戶商這時候會生成一個新的對稱密鑰, 並使用服務器的公共密鑰進行加密, 並告訴服務器,未來的通信以加密的方式進行。 到目前爲此,所有交換過的數據都是以明文的方式進行, 除了新的對稱密鑰, 是使用服務器的公共密鑰進行了加密。

140ms 服務器使用自己的私鑰對客戶端發過來的 對稱密鑰進行解密, 並通過MAC確認消息的完成性, 並使用(用解密後的對稱密鑰), 加密 "Finished" 信息,並返回給客戶端.

168ms 客戶端之前的生成的對稱密解, 解密服務器返饋的信息, 並覈實MAC是否一致。 如果都是正確的, 則建立起了安全加密鏈路, 應用數據現在就可以發送。

你可以通過 http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html 瞭解更詳細的握手過程

新的TLS連接需要兩次的往返(上圖中的綠色線),才能完成整個握手過程。 另外,你可以選擇一種簡單的握手, 它只需一次往返, 可以查看 55頁的 ”TLS Session Resumption"

協商建立一條安全的TLS 遂道是一個複雜的過程, 許多的子過程都可能會導致錯誤的發生。 但好的消息是,所有的工作都是通過瀏覽器和服務器完成, 我們只需要提供和配置自己的證書。

話雖如此, 我們的應用程序不用管TLS 遂道的建立, 但是它必須等待整個TCP, TLS 握手的完成, 纔可以發送數據。 如果不精心管理, 在TLS上傳遞數據, 會引入上百,上千毫秒的延時。

應用層協議協商(ALPN)

網絡的兩端希望使用自定義的應用層協議,彼此進行通信。 可以直接通過端口進行確定, e.g HTTP使用 80端口, TLS  使用  443端口。 所有的客戶端和服務器都是使用的這些公認的端口。但在實際中,通過端口來配匹配應用層協議是不可靠的,每一個端口都需要由系統開發, 而且防火牆和其它的中間機構只允許 80 和 443端口的信息通過。

所以, 爲了可以簡單的部署自定義的協議, 我們必須重用80  或者 443端口, 並且可以使用額外的機制協商應用程序協議。 80 端口是爲HTTP保留, HTTP規範也指出,新的協議可以運行在HTTP協議層之上。但這會添加額外的RTT 和 延遲。 並且在實際中,也是不可靠的。 可以查看   “Proxies, Intermediaries, TLS, and New Protocols on the Web” 原書50頁.

那麼剩下的解決方案就是443端口, TLS運行在這個端品。 它使用端到端加密遂道,對經過的數據進行模糊處理, 所以中間機構不需要知道傳遞的是什麼類型的協議數據, 這樣允許快速而且可靠的建立任意的應用層協議。 可是, 使用TLS 解決了可靠性的問題, 我們依然需要有一種方法,協商新的應用層協議。

我們可以將協商新的協議(HTTPS),作爲TLS 握手的一部分:
  • 客戶端向ClientHello消息中,添加一個, 新的ProtocolNameList 字段, 這個字段包含了它所支持的應用協議列表
  • 服務器檢查ProtocolNameList  字段, 並在返回的SeverHello 消息中包含 ProtocolName字段,包含選中的協議
服務器可能會返回單個的協議名稱, 如果它不支持客戶端要求的協議。 則會選擇終止連接。 因此,一旦TLS握手完成, 雙方之間的安全遂道已經建立, 同時客戶端和服務器也協商好了要使用到的應用程序協議, 它們即可以立即用協商的進行通信。

ALPN 沒有通過HTTP Upgrade exchange 的方式,減小了RTT導致的延時, 但需要注意, TLS握手自身也會產生一個響應延時, 因此 , ALPN 協商並不會比 HTTP Upgrade快,只是它建立的是一個加密,哥靠的協議。

Server Name Indication (SNI)

在TCP兩端建立好的TLS遂道, 客戶端只需要知道服務器的IP地址就可以執行握手協議,但如果一臺服務器有多個站點, 哪一個網站纔是需要建立TLS連接呢?

爲了解決這個問題, Server Name Indication(SNI) 作爲一個擴展,被引入到TLS協議, 它允許客戶端在握手開始的時候,通過hostname字段,標識出它想要建立服務器的名稱。 web 服務器會檢查SNI 中的 hostname. 並選擇合適的證書, 並繼續完成握手。

但在許多老的客戶端並不支持SNI,  比如運行在 Windows XP的瀏覽器, Android 2.2 等。

TLS Session Resumption

在所有使用安全通信的應用程序中, 握手階段導致了額外的延遲和CPU計算, 嚴重的影響了應用程序的性能。爲了減小相同的浪費, TLS 提供了在多個連接中,共用相同的安全密鑰(生成的對稱密鑰)的能力。


Session Indentifiers  

會話標識重用是在SSL 2.0版本被引入, 它充許服務器創建併發送 32 字節的會話標識,作爲 ”ServerHello" 消息的一部份。

在服務器內部,它需要爲每一個客戶端緩存 session ID 以及TLS連接參數 (key,  加密算法)。 同樣, 客戶端也需要保存sessionID 信息, 並且在下次的TLS請求中, 在ClientHello 消息中帶上sessionID.  當服務器收到sessionID時, 就知道客戶端依然保存了之前加密套件和 從上次握手所獲得的 對稱密鑰。如圖 4-3所示, 一個簡短的握手過程。如果雙方沒有從緩存在找到之前的信息,那麼將會產生一個新的 session ID.


圖4-3 簡短的握手過程


利用會話標識,讓我們減小了一個RTT時間, 以及計算開銷。

在實際工作中, 許多web應用程序, 爲了獲取資源 會以並行的方式向相同的服務器建立連接, 就必須使用 會話重用,以減小延遲和計算開銷。 現代化的瀏覽器會有意的等待第一個TLS連接完成,在打開一條新的連接。

可是,在實際的工作中,使用服務器創建和保存session  標識符 具有侷限性。 比如每天都會有成千上萬的連接需要保存到緩存,這會導致緩存被使用完畢, 有的網站是採用多個服務器,如果共享這些session也是一個問題。 (session  保存在服務器會帶來哪些實際問題,可以查看 http://nil-zhang.iteye.com/blog/1279214)

Session Tickets

爲了不在服務器緩存 TLS 的 sesion., 有了 "Session Ticket" (RFC5077) 機制, 它不需要服務器爲每一個已連接的客戶端保存 session 狀態。 代替的是, 如果客戶端表明,它支持 Session Tickets, 在 TLS  握手的最後, 服務器會包含進一個新的  Session Ticket record. 這個記錄包含了所有會話數據, 並且會以服務器才知道的安全密解加密。

會話船票會保存在客戶端, 在之後的連接中,會以  SessionTick 擴展,包含進ClientHello 信息中。 這樣所有的信息就僅保存在客戶端, 而且 ticket 依然是安全的, 因爲它是使用服務器才知道的密鑰加密的。

session indentifiers 和 session ticket 機制可以分別稱爲  會話緩存 和 無狀態恢復。 無狀態恢復的最大改進是不需要服務器端緩存, 客戶端只需在新的連接中提供 session ticket, 除非ticket 已經過期。


信息鏈 和 CA

我們怎樣才能知道,在建立加密遂道是我們信任的一方,而不是一個攻擊者, 所以身份證認證在TLS 連接中是不可分割的一部分。爲了知道如何驗證雙方的身份, 我們舉了在 Alice 和 Bob  之間的一個例子:

  • Alice 和 Bob 都有自己的公共密鑰和私有密鑰
  • 雙方都會隱藏自己的私有密鑰
  • Alice 向 Bob 分享自己的公共密鑰, Bob 也向 Alice 分享了自己的公共密鑰
  • Alice 向 Bob 發了一條消息,這條信息是用她自己的私有密鑰加密的
  • Bob 使用 Alice's 的公共密鑰來校對信息提供者的簽名, 這條消息是由Alice發送過來,而不是其它人

信任是交換數據之前的關鍵, 公鑰加密算法,允許我們使用信息發送者的公共密鑰, 確認被簽名的信息是正確人發送過來的。  但這種信任完全是基於 Alice 和 Bob 相互認識的基礎上,才交換的公共密鑰。

下一步, Alice 從Clarlie那裏接收到一條信息, Alice 沒有見過Clarlie,   但Clarlie 聲稱它是 Bob's 的朋友。 實際上, Clarlie 爲了證明自己是Bob的朋友, Clarlie 要求Bob 對自己的公開密鑰 使用 Bob 的私有密鑰進行籤時。 並以消息的附件,一起發送給Alice.  如圖4-4.  在這種情況下, Alice 首先確認 Clarlie的公共密鑰中Bob的簽名。 她已經有了Bob 的公開密鑰, 所以知道Clarlie 的公開密鑰的確是由 Bob簽過的。Alice 接受了這條消息, 並使用消息中的Clarlie 公開密鑰, 確證發送條消息的人確實是Clarlie.

圖4-4


這樣我們就建立了一條信息鏈: Alice 信任 Bob, Bob 信任 Charlie. 並且通過傳遞信任, Alice 確定信息 Charlie.

互聯網和你的瀏覽器之間的認證也是採用相同的處理, 瀏覽器可以通過以下幾種方式,添加信任
  •  手動指定證書: 每一個瀏覽器和操作系統都提供了一種機制,你可以手動的導入你信息的證書
  • CA認證中心的證書
  • 瀏覽器和操作系統自帶的證書

在實際中,你不可能存放所有網站的證書。因此最通用的解決方法是使用CAs來幫我們進行校驗.  如圖 4-5.  網站會讓認證中心對他的名字和公鑰進行簽名,並將這個簽名發送到瀏覽器,如果瀏覽器的CAs的根目錄下,有認證中心的公開密鑰,就能確認這個網站是真實的。



圖4-5
每一個瀏覽器都允許你檢查你安全連接中的信任鏈, 你可以當擊URL 的中鎖, 如圖 4-6

圖 4-6

Certificate Revocation

不重要,忽略

TLS Record Protocol

不同於IP 或者 TCP協議, 在TLS會話中的所有數據交換都是由TSL Record協議負責。 該協議需要負責識別不同類型的消息(握手信息,警告,數據), 並校對每一個信息的完整性。

圖 4-8 TLS record  結構

傳遞應用程序的數據的流程如下:

  • Record  協議獲得應用程序的數據
  • 對接收到的數據進行分塊: 最大爲2的14次 bytes 或者16KB一個記錄
  • 應用數據可以選擇壓縮
  • Message autentication code(MAC) 或 HMAC, 添加消息的校驗碼
  • 使用協商好的加密算法,對數據進行加密

完成以上步聚, 被加密的數據就會向下傳入TCP層,進行傳輸。 在接收端, 則是相反的過程。

這個過程看起來很簡單,但需要注間幾個地方:

  • TLS record 最大爲16KB
  • 每一個記錄會包含 5-byte header,   一個MAC (SSLv3, TLS1.0, TLS1.1 爲 20 bytes, TLS 1.2 需要32字節), 如果使用了塊分組密碼, 還需要把密碼加上。
  • 爲了解密和校對record, 整個record 必須有效。

爲你的應用程序挑選出最佳的record 的大小,對性能的優化非常重要,小的records 會導致很大的開銷,  然而大的records 會被TCP重新進行組裝

優化TLS

TLS的優化主要是配置你的服務器,比如TLS records 的大小, 內存緩衝區, 證書的大小, 是否支持簡短的握手等等, 通過對這些參數的正確配置,會明顯改善用戶體驗,降低操作開銷。

計算開銷

建立和維護一個加密的通道,在連接的兩端都會引入額外的計算開銷, 特別是在TLS握手時使用公共密鑰加密對稱密鑰。 當連接建立之後,可以使用對稱密碼加密所有的TLS records.

我們之前說過, 使用公鑰加密 對比 對稱密鑰加密 會導致非常大的計算開銷。 早先的網站通常需要額外的硬件來完成 SSl計算。但現在的硬件的性能有很大的提升,都能直接完成
但對於TLS Session 的恢復, 還是可以減少使用公鑰加密。 除低計算的開銷。

提前終止

 不管是新建還是重用一個連接,都會有延遲的發生, 對於優化來說, 連接的建立是一個重要的區域。  我們看看一個TLS連接有哪些: TCP 三次握手, 之後是TLS握手, 增加兩個RTT時間(新建)。 或者一個RTT(重用)。

在最壞的情況下,數據交換之前, TCP 和 TLS連接的設置過程就將花費三個 RTT.  以我們之前New York到London的例子, 一個RTT 時間爲56ms, 那麼新建一個TLS 將花費168ms, 而重用一個將花費112ms.  

因爲TLS是運行在TCP之上,所以對TCP的優化,也適用於這裏。 通過CDN將服務器位置到離客戶端最近的地方,減小RTT的值。 CDN不僅可以優化靜態資源,你也可以應用動態內容上,提前終止TLS 會話。 如圖 4-9所示,在本地代理服務器上,建立與客戶端的連接(加速完成握手),代理服務器與原始服務器之間建立一個長久(沒有握手,只負責數據傳送),加密的連接。這樣所有的請求和響應都是從原服務器獲取

圖4-9 提前終止客戶端連接

要做到這一點非常容易,很多CDN都提供這樣的服務, 你也喜歡冒險,也可以以最小的花費搭建自己的基礎設施: 在全球各地的數據中心部署雲服務器,並配置代理服務器,將請求轉發到你的原始服務器中。 並可以加入基於地理位置的DNS 負載均衡。


Session Caching and Stateless Resumption

瞭解概念即可

TLS Record Size

通過TLS傳輸的應用數據,都是使用 record 協議, 查看圖 4-8.   每一個記錄都將添加20-40不等字節大小的頭部信息, MAC 信息,以及其它的信息。 如果一個記錄剛好可以裝入一個 TCP數據包, 我們還需要添加IP 和 TCP的信息, 比如 IP會有20字節的頭部信息, TCP也會有20字節的頭部信息。 最終, 每個記錄大概會添加60-100字節的頭部信息。 電路中一個標準的最大傳輸單位(MTU) 大小是1500 字節。 數據包的結構就佔據了 6% 的大小(overhead,在計算機網絡的幀結構中,除了有用數據以外,還有很多控制信息,這些控制信息用來保證通信的完成。這些控制信息被稱作系統開銷).

如果是一個很好的記錄,那麼就不是 6%的系統開銷,還是更高的比例。 可是,簡單的增加記錄的值,使它達到最大允許傳遞的16KB, 這也不是一個很好的方法。 如果一個record 非常大,它會被分裝進多個TCP 數據包, 則TLS的另一端必須等待所有的TCP包到達之後纔可以對數據進行解碼(如圖4-10)。  如果某些TCP數據包丟失, 還需要重傳, 造成額外的延遲。 在實際中, 這種延遲對於瀏覽器造成很大的延遲,  還不如對數據一個字節一個字節的傳遞, 這樣還可能會更快。


圖 4-10. WireShark capture of 11,211-byte TLS record split over 8 TCP segments

如果records 太小,增加系統開銷, 傳到另一頭的有用數據就越少。相應的,傳送可用數據越少意味着傳送的數據要更多的數據包。這還意味着完成數據傳輸需要額外的時間。 如果是大的records 又會引發延遲的發生。   這裏很難找到一個“正確” 的 record大小。   還好,瀏覽器會自動將web應用程序的值,設置爲TCP MSS的大小,即records 值爲 一個MSS(1460 bytes). 這樣的個TCP數據包傳遞一個record, 這樣一個record就可以分配到多個TCP 數中包當中。 通過以下的方式,我們能計算出一個最佳的record:
  • 電路中傳輸的數據包, 會包含IPv4 20bytes 地址, 如果是IPv6會是40 bytes
  • TCP 會有20 bytes頭部信息
  • 40 個字節的TCP 可選信息, 比如 timestamps, SACKs

MTU通用的大小爲1500 bytes,  那麼在IPv4中 TLS record 大小就是1420 (1500- IP 20 - TCP 20 - TCP Option 40), 而對於IPv6,這個大小爲1400.   爲了兼容未來, 所以最佳的值爲1400.

但我們在服務器的應用層上是不能配置 TLS  record 的值。而是需要通過服務器級別的配置,具體方法,需要查看服務器系統文檔。

如果你的服務器需要處理大量的TLS 連接,則需要爲每個連接分配最小的內存使用。 OpenSSL 通常會爲每個連接分配50KB 的內存, 但正如調整record 大小一樣,最好還是查看OpenSSL 的文檔。 Google的服務器能常將OpenSSL的值設置爲5KB.

TLS 壓縮

TLS 內部通過 record協議, 支持對傳輸的數據進行無損壓縮: 壓縮的算法是通過握手協議,雙方協商好的。 壓縮會發生在對每條record 加密之前。 但通常,你要禁止服務器的壓TLS 壓縮功能, 有以下幾個原因:

  • 在2012年, 黑客利用過TLS 壓縮獲得加密認證的cookie,  這就允許黑客執行會劫持, 這種功能方法叫作"CRIME"
  • 傳輸級別上的TLS 不知道要壓縮的內容是什麼,可能會壓縮已經壓縮過的數據,比如,圖片,視頻

兩次的壓縮,會浪費服務器和客戶端的CPU,  而且導致非常嚴重的安全問題, 雖然很多的瀏覽器禁用了TLS 壓縮,但爲了更好的保護你的客戶, 你需要明確的禁止服務器壓縮.

認證鏈長度

瀏覽器驗證證書的過程是: 從網站的證書開始, 在遍歷父證書, 直到信任的根目錄( 證書是分級的,全球, 國家, 省份).  因此,第一條優化的原則是,在服務器上配置所有的中級證書。 如果忘記了, 那麼許多瀏覽器依然可以工作,但是它會暫停, 等待, 並向中間證書的服務器, 獲取中間證書, 然後繼續驗證,直至根目錄信任的證書。 這過程中會有新的DNS 查詢, TCP連接, 和 HTTP GET請求, 會有數百毫秒的握手延遲。

可是瀏覽器怎麼知道如何獲取中間證書的呢? 每一個子證書通常都包含了父證書的URL

相反的, 你必須在你的信任鏈裏不會包含沒必要的證書, 或者通俗點說, 你應該減少信任鏈的大小。 回憶一下, 服務器在TLS 握手期間, 會向客戶端發送證書, 很有可能證書的發送是在發生在新TCP連接的slow-start 階段。 如果證書鏈的大小超過了TCP 初始 cwnd 的大小, 則只能先發送一部份(TCP cwnd大小), 等待客戶端的 ACK後,在發送剩下的部分, 這就會導致多個RTT時間。 如圖 4-11

圖4-11 WireShack 軟件截圖 TLS 證書鏈的大小爲 5, 323-byte

圖4-11中, 證書鏈超過了5KB的大小,它超過了一些的老的服務器的擁塞窗口的大小。這就在握手階段引入了其它RTT的大小。 有一個解決方案是增加初始的擁塞窗口的大小。 可以查看 "Increasing TCP's Initial Congestion Window "  原書 26頁。 另外, 你也可以減少證書的大小:
  • 減小認證的層級, 理想的是隻包含你自己的證書和中間人證書,第三個證書就是根目錄證書(瀏覽器已經信任, 不需要在發送)
  • 不要發送root 證書, 如果你的瀏覽器連root證書都沒有,那麼你發了,它也會不信任它
  • 將證書鏈的大小減小到  2 KB 或 者3KB, 雖然爲瀏覽器提供所有必要的證書,可以避免瀏覽器自己發起新的連接,獲取中間代理人證書,從而引發不必要的RTT.  但這可能也就發生一次,而過大的證書會導致每一次新的TLS連接,有會有額外的RTT, 這種性能損失更大

OCSP 裝訂

針對OCSP的優化, 每一個新的TLS連接都需要瀏覽器檢測認證鏈,但有一個步聚我們不能忘記, 那就是瀏覽器還需要檢測證書是否被呆銷,在這裏,瀏覽器有兩個方法,一個是下載CRL文件並且緩存, 還有一個就是通過OCSP進行實時的檢測, 從認證中心的服務器獲取響應, 並使用認證中心的公開對響應進行加密。 以證實證書是否吊銷。所以我們可以在服務器 發送一個請求到認證中心的響應,並且把它包含(裝訂)到認識鏈中(部分瀏覽器可以識別)。 這樣我們自己的服務器就可以緩存被簽名過的OCSP響應,可以節省多個客戶端的額外請求。 但這裏也需要注意一些問題:
  • OCSP的大小從400到 4000不等, 裝訂到你的認證鏈中, 可能會超過你TCP擁塞窗口的大小
  • 只能裝訂一個OCSP響應, 那麼對於中間(發證中心)的證書,瀏覽器還是會發送OCSP請求。

最近,你需要在配置服務器,允許有OCSP裝訂的功能。 好的消息是,主流的服務器, 比如Nginx, Apache, IIS是有這個功能的。 你可以查看這些服務器的文檔說明。


HTTP Strict Transport Security (HSTS)

HTTP Strict Transport Security  是一個安全策略機制, 它允許服務器通過簡單的HTTP 頭, 比如 Strict-Transport-Security: max-age = 3153600. 向順從的瀏覽器(知道這類http頭的瀏覽器) 聲明訪問規則。 這條規則表示,客戶端必須強制執行以下規則:

  • 所有到初始請求,都必須使用HTTPS
  • 所有不安全的連接 和客戶端請求,在請求發送之前, 應該自動轉化爲 HTTPS
  • 如果發生認識錯誤, 顯示錯誤信息, 並且不允許繞過吃警告
  • max-age 表明 HSTS規則的有效期, 單位爲秒

HSTS 不僅可以將原始連接轉變爲 HTTPS,  還可以保護應程序, 在性能上, 可以減小從 HTTP-to-HTTS的跳轉(301 或者302)。

 在2013年時, 支持HSTS的瀏覽器有 Firefox 4+, Chrome 4+, Opera 12+  以及Android 版的Chrome 和 Firefox. 最新的瞭解可以查看 caniuse.com/stricttransportsecurity.

TLS 性能檢測清單

  • 優化TCP, 可以查看 "Optimizing for TCP" 在原書第32
  • 升級TLS 庫到最新的版本
  • 允許並配置 TLS session 的緩存 和 無狀態重用
  • 監測你的TLS session 緩存命中率, 並調整相應的配置
  • 使用CDN, 在客戶端最近的寺方, 提前終止 TLS  sesion, 降低往返導致的延遲
  • 配置TLS record 的大小,以適應單個TCP段的大小, 1400 bytes
  • 確保你的認證不會超過初始化擁塞窗口的大小, 低於 2 or 3KB
  • 從認證鏈中刪除沒必要的證書, 減小認證鏈的深度
  • 禁用服務器的TLS 壓縮
  • 配置你的服務器,以支持SNI,  域名識別
  • 配置OCSP 裝訂功能
  • 添加  HTTP Strict Transport Security header

測試和檢驗

最後, 檢驗和測試你的配置, 你可以使用在線的服務, 比如 Qualys SSL Server Test 掃描你公開的服務器的通用配置 和安全缺陷。  另外,你也可以使用 openssl 命令, 它將幫助你檢驗整個握手過程和本地的服務器配置

$> openssl s_client -state -CAfile startssl.ca.crt -connect igvita.com:443
CONNECTED(00000003)
SSL_connect:before/connect initialization
SSL_connect:SSLv2/v3 write client hello A
SSL_connect:SSLv3 read server hello A
depth=2 /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing
/CN=StartCom Certification Authority
verify return:1
depth=1 /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing
/CN=StartCom Class 1 Primary Intermediate Server CA
verify return:1
depth=0 /description=ABjQuqt3nPv7ebEG/C=US
/CN=www.igvita.com/[email protected]
verify return:1
SSL_connect:SSLv3 read server certificate A
SSL_connect:SSLv3 read server done A
SSL_connect:SSLv3 write client key exchange A
SSL_connect:SSLv3 write change cipher spec A
SSL_connect:SSLv3 write finished A
SSL_connect:SSLv3 flush data
SSL_connect:SSLv3 read finished A
---
Certificate chain
0 s:/description=ABjQuqt3nPv7ebEG/C=US
/CN=www.igvita.com/[email protected]
i:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing
/CN=StartCom Class 1 Primary Intermediate Server CA
1 s:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing
/CN=StartCom Class 1 Primary Intermediate Server CA
i:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing
/CN=StartCom Certification Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
... snip ...
---
No client certificate CA names sent
---
SSL handshake has read 3571 bytes and written 444 bytes
---
New, TLSv1/SSLv3, Cipher is RC4-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : RC4-SHA
Session-ID: 269349C84A4702EFA7 ...
Session-ID-ctx:
Master-Key: 1F5F5F33D50BE6228A ...
Key-Arg : None
Start Time: 1354037095
Timeout : 300 (sec)
Verify return code: 0 (ok)

  1.  客戶端完成 認證鏈的驗證
  2.  接收到認證鏈(兩個證書)
  3. 接收到的認證鏈的大小
  4.  有狀態的TLS session 標識符

在此之例子中, 我們通過TLS的默認端口(443) 連接到 igvita.com , 並執行TLS 握手。 由於 s_client 不清楚root證書,我們需要手動將 StartSSL Certifiecate Authority 導入到根目錄(非常重要的一步) , 否則 s_client 會看到一個校驗失敗的錯誤日誌。

檢測證書鏈的過程中,我們看到服務器發送了兩個證書, 總的大小爲3,571 bytes, 非常接進 3 到 4 個段 (TCP 老的服務器初始擁塞窗口的大小爲 4 個段) 。最後, 我們檢測的協商 SSL 的變量 - 最新的協議, 加密算法 , 對稱密鑰 - 我們也可以看到服務器發磅了一個session 標識。


發佈了44 篇原創文章 · 獲贊 11 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章