1. 程式人生 > >SSL/TLS深度解析--TLS效能優化

SSL/TLS深度解析--TLS效能優化

TCP 優化

Linux系統核心引數優化

[[email protected] ~]# cat /etc/redhat-release;uname -r
CentOS Linux release 7.5.1804 (Core) 
3.10.0-862.11.6.el7.x86_64

TLS 的下一層是 TCP 協議,所以對 TCP 的優化是可以直接影響到 TLS 的效能效率。

在對 TCP 的優化中,主要涉及以下幾個概念:

(1)擁塞控制(congestion control)機制:在一個 TCP 連線開始時,不知道對方的速度有多快。如果有足夠大的頻寬,伺服器端可以用最快的速度傳送資料,但是如果對方的網路很慢,伺服器傳送的資料太多的話,會壓跨連線,導致連線中斷。所以,每個 TCP 連線都有一個稱為擁塞視窗(cwnd = congestion window)的速度極限。這個視窗最初較小,在通訊的過程中,如果雙方都能接受這個速度,那麼會加大這個擁塞視窗的值(初期增長很快,翻倍增長),這種機制被叫做慢啟動(slow start)。

擁塞控制機制對於 TLS 連線的影響比較大,TLS 握手消耗了寶貴的初始連線位元組(當擁塞視窗較小時);如果擁塞視窗足夠大,那麼慢啟動不會有額外的延遲。但是,如果握手訊息長度超過了擁塞視窗大小,傳送方將必須把這個長資訊拆分成兩塊,先發送一塊,等待確認(1個往返),增加擁塞視窗,然後再發送剩下的部分。這樣就增加了由 TLS 握手造成的延時。

(2) 慢啟動閾值 ssthresh(避免 cwnd 增長過快,網路無法承擔,造成丟包)

如果 cwnd 小於 ssthresh,表示在慢啟動階段,cwnd是翻倍增長的;如果 cwnd 大於 ssthresh,那麼表示在擁塞避免階段,這時候 cwnd 不再像慢啟動階段那樣翻倍增長,而是線性增長,儘量避免網路擁塞。

(3) 接收視窗(rwnd),用來表示最多能儲存多少資料,實際中接收視窗rwnd的合理值取決於BDP的大小,也就是頻寬和延遲的乘積。如果頻寬是 80Mbps,延遲是 100ms,那麼計算過程如下:

BDP = 80Mbps 100ms = (80 / 8) (100 / 1000) = 1MB = 1024KB = 1048576B

TCP 用16位來記錄視窗大小,也就是說最大值是64KB,如果超過這個值,就需要 tcp_window_scaling 機制(預設是開啟)。配置核心引數中接收緩衝的大小,就可以控制接收視窗的大小:

net.ipv4.tcp_rmem = <MIN> <DEFAULT> <MAX>

Linux本身有一個緩衝大小自動調優的機制,視窗的實際大小會自動在最小值和最大值之間變化,找到效能和資源的平衡點。確認緩衝大小自動調優機制(0:關閉、1:開啟):sysctl -a | grep tcp_moderate_rcvbuf。如果緩衝大小自動調優機制設定成關閉狀態,那麼就把緩衝的 DEFAULT 值設定為 BDP;如果緩衝大小自動調優機制設定成開啟狀態,那麼就把緩衝的 MAX 設定為 BDP。

(4) 儲存 TCP 連線本身一些資訊的額外開銷:net.ipv4.tcp_adv_win_scale 的值可能是 1 或者 2,如果是 1 的話,則表示二分之一的緩衝被用來做額外開銷,如果是 2 的話,則表示四分之一的緩衝被用來做額外開銷。按照這個邏輯,緩衝最終的合理值的具體計算方法如下:result=BDP / (1 – 1 / 2^tcp_adv_win_scale)。

(5) 空閒連接回到慢啟動:慢啟動在一段時間內沒有任何流量的連線上起作用,達到降低速度的效果,並且速度下降非常快。所謂的“一段時間”可以是非常小的,比如1秒鐘,但在實際場景中,每一個長連線(例如使用 HTTP 長連線)的速度都有可能被調到很慢!為了保持速度建議禁用這個功能。

在 Linux 上,可以在連線空閒時禁用慢啟動: 0表示否,1表示開啟慢啟動,預設是1

可以通過一下命令使其臨時生效,但重啟以後就失效了,檢視:sysctl -a | gerp slow_start_after_idle

臨時:sysctl -w net.ipv4.tcp_slow_start_after_idle=0

永久生效:將 net.ipv4.tcp_slow_start_after_idle=0 設定新增到 /etc/sysctl.conf 配置檔案中。

對擁塞視窗(cwnd)初始值調優:

啟動速度限制被稱為初始擁塞視窗(initial congestion window, initcwnd )。2013年4月釋出的 RFC6928,google 建議預設情況下初始擁塞視窗設定為10個 MSS(約15 KB)。【Centos 7預設是10MSS】早期的建議是使用2或4個MSS(約3—6KB)。MSS 是 TCP 層上的概念,大小是 1460 位元組。IP 層上是 MTU,1500位元組。

[[email protected] ~]# sysctl -a |grep ssthresh
net.ipv4.tcp_max_ssthresh = 0   #在虛擬機器中
[[email protected] ~]# sysctl -a |grep tcp_window_scaling
net.ipv4.tcp_window_scaling = 1
[[email protected] ~]# cat /proc/sys/net/ipv4/tcp_rmem    # rwnd值
4096    87380   6291456
[[email protected] ~]# sysctl -a |grep tcp_moderate_rcvbuf        
net.ipv4.tcp_moderate_rcvbuf = 1
[[email protected] ~]# sysctl -a |grep adv_win_scale
net.ipv4.tcp_adv_win_scale = 1
[[email protected] ~]# sysctl -a |grep start_after_idle
net.ipv4.tcp_slow_start_after_idle = 1

# 設定cwnd
[[email protected] ~]# ip route
default via 172.16.216.2 dev ens33 
169.254.0.0/16 dev ens33 scope link metric 1002 
172.16.216.0/24 dev ens33 proto kernel scope link src 172.16.216.188 
[[email protected] ~]# ip route | while read p; do ip route change $p initcwnd 10; done
[[email protected] ~]# ip route
default via 172.16.216.2 dev ens33 initcwnd 10 
169.254.0.0/16 dev ens33 scope link metric 1002 initcwnd 10 
172.16.216.0/24 dev ens33 proto kernel scope link src 172.16.216.188 initcwnd 10
# initcwnd 10:初始化cwnd
# 單方面提升傳送端 cwnd 的大小並不一定有效,因為網路中實際傳輸的未經確認的資料大小取決於  rwnd 和 cwnd 中的最小值,所以一旦接收方的 rwnd 比較小的話,會抑制 cwnd 的發揮。

# 設定initrwnd(linux kernel 2.6.33 and newer)
[[email protected] ~]# ip route
default via 172.16.216.2 dev ens33 
169.254.0.0/16 dev ens33 scope link metric 1002 
172.16.216.0/24 dev ens33 proto kernel scope link src 172.16.216.188 
[[email protected] ~]# ip route | while read p; do ip route change $p initrwnd 10; done 
[[email protected] ~]# ip route
default via 172.16.216.2 dev ens33 initrwnd 10 
169.254.0.0/16 dev ens33 scope link metric 1002 initrwnd 10 
172.16.216.0/24 dev ens33 proto kernel scope link src 172.16.216.188 initrwnd 10 

# 一些系統的rwnd值:
# Linux 2.6.32                                      3*MSS (usually 4380)
# Linux 3.0.0                                       10*MSS (usually 14600)
# Windows NT 6.1    (Windows 7 or Server 2008 R2)   8192 ^ 2
  • 優化 tcp time_wait ,減少time_wait 狀態的連線。主動關閉的一方會出現time_wait狀態。

    time_wait 狀態的連線要等待2個 MSL 的時間才會 close,會佔用資源,儘量避免連線進入 time_wait 狀態。linux 裡 MSL一般是30秒,2 個MSL 是1分鐘,這個數值是硬編碼在核心中的,除非重新編譯核心,否則沒法修改。注:MSL最長報文生命週期:Maximum Segment Lifetime,MSL 。

    修改 fin_wait2 的值,減少 fin_wait2 的等待時間,超時以後會回收連線。

    開啟長連線:絕大多是瀏覽器在開啟長連線的情況下,接收到伺服器斷開連線的fin以後,會恢復一個 ack;而不會不傳送自己這一端的 fin ,這樣伺服器一端就會等待 fin_timeout 時間後,回收連線。

    若不開啟長連線,伺服器端關閉連結以後,連結的狀態會從 fin_wait2 轉換到 time_wait 。

    還可以考慮促使客戶端關閉連結,配置 keepalive_timeout 20s 10s; (nginx 配置),使客戶端的超時小於伺服器端,瀏覽器會先關閉連結,這樣time_wait 狀態就會在客戶端,不過通過實驗看出只有火狐瀏覽器支援,狐火瀏覽器會識別 Keep-Alive: timeout=time 這個引數,而別的瀏覽器不會。

    不要設定回收 recycle=1 和 重用 reuse=1,NAT模式下會造成連線失敗( SYN 包不會被響應)

    time_wait 狀態的連線被重用(reuse)的條件是如下2個之一:

    1)初始序列號比time_wait狀態的老連線最末的序列號大。

    2)如果使用時間戳,那麼新到來的連線的時間戳比老連線的時間戳大。

    tcp_tw_reuse和tcp_tw_recycle要生效,必須 tcp_timestamps 是開啟的,預設也是開啟的。

  • 引數優化

    net.ipv4.tcp_max_syn_backlog = 1024 #SYN佇列的長度,預設是1024,加大佇列到8192或更大,可快取更多等待的網路連線。

    net.ipv4.tcp_max_tw_buckets = 180000 #儲存 TIME_WAIT 狀態的套接字的最大數量,一旦超過這個數,TIME_WAIT套接字將立刻被清除,併發出警告。

    net.ipv4.ip_local_port_range = 1024 65535 # 向外連線的埠範圍。預設值:32768到61000,可以擴大 1024 到 65535。

    net.ipv4.tcp_syncookies = 1 #開啟SYN Cookies,SYN等待佇列溢位時,使用cookies來處理,可防範少量SYN***。

    net.ipv4.tcp_retries2 = 15 #TCP失敗重傳的次數 ,預設15 ,可以調小一些,例如5。

    還可以配置用於 TCP/IP 連結所使用的記憶體,配置總記憶體的話,單位是“頁” ,具體的一個頁的大小可以通過 getconf PAGE_SIZE 這個命令獲取;讀寫所佔用的記憶體單位是位元組。

[[email protected] ~]# getconf PAGE_SIZE
4096

總記憶體

net.ipv4.tcp_mem = 93408 124544 186816

寫(緩衝)

net.ipv4.tcp_wmem = 4096 16384 3985408

讀(快取)

net.ipv4.tcp_rmem = 4096 87380 3985408

[[email protected] ~]# cd /proc/sys/net/ipv4
[[email protected] ipv4]# cat tcp_fin_timeout 
60
[[email protected] ~]# sysctl -a |grep timestamps
net.ipv4.tcp_timestamps = 1

initcwnd

ip-sysctl

SSL/TLS深度解析--TLS效能優化

TLS 協議優化

  • 對TLS協議進行安全和速度調優

    1.金鑰交換

    使用TLS最大的成本除了延遲以外(多了2次往返),就是用於安全引數協商的CPU密集型操作,也就是金鑰交換(key exchange)。金鑰交換的CPU消耗很大程度上取決於伺服器選擇的私鑰演算法、金鑰長度和金鑰交換演算法。

    破解金鑰的難度取決於金鑰的長度,金鑰越長越安全。但是也要考慮加密與解密所消耗的計算資源。目前有兩種私鑰演算法可以使用:RSA和ECDSA。

    現在RSA演算法的金鑰仍然大量存在,即使使用它進行金鑰交換的時候不支援前向加密。但是RSA還是可以用在身份認證上,當前RSA金鑰演算法推薦最小長度2048位,並且考慮升級到3072位(雖然升級後效率下降較多),隨著RSA金鑰的增長它開始變得越來越慢。ECDSA會快很多,越來越多的站點支援ECDSA,中等長度256位的ECDSA提供與3072位RSA一樣的安全性,卻有更好的效能。

    推薦優先使用:ECDSA256_ECDHE256 與 RSA2048_ECDHE256 。

    SSL/TLS深度解析--TLS效能優化

    2. 證書

  • 證書鏈

    a、TLS握手的時候,伺服器端會把證書鏈傳送給客戶端進行驗證。

    b、證書鏈儘可能短。

    c、證書鏈要完整。

    d、儘量使用橢圓曲線證書鏈。

  • 證書吊銷檢查與OCSP服務

    雖然證書吊銷狀態在不斷變化,並且客戶端(瀏覽器)對如何檢查證書吊銷差異很大,但作為伺服器端,要做到儘可能快的傳遞吊銷資訊。

  • 使用帶OCSP資訊的證書。

    OCSP被設計用於提供實時查詢,允許客戶端訪問吊銷資訊。因此查詢簡短而快速(1個HTTP請求)。相比之下CRL是一個包含大量被吊銷證書的列表。一些客戶端只有當OCSP資訊不可用的時候才下載CRL,在下載CRL的時候瀏覽器與伺服器端的通訊將暫停,直到CRL下載完成,所消耗的時間可能會有幾十秒。

  • 選擇具有快速且可靠的OCSP響應程式的CA

    不同CA之間的OCSP響應速度也不同。緩慢和錯誤的OCSP響應程式會潛在地導致效能下降。在決定使用OCSP響應之後,要考察CA對OCSP響應的效能與正確性。另一個選擇CA的標準是看更新OCSP響應的速度,最好自己的證書一經頒發就加入到OCSP響應程式中,一旦出了安全隱患被吊銷,OCSP響應也能迅速的更新。

[[email protected] ~]# openssl s_client -connect www.openssl.org:443 -status |grep -i ocsp
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify error:num=20:unable to get local issuer certificate
OCSP response: 
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response

SSL/TLS深度解析--TLS效能優化

3. 會話恢復

TLS理解兩種型別的握手:完整握手和簡短握手。理論上完整握手只會在客戶端與伺服器建立TLS會話(TLS session)的時候進行一次。後續的連線,雙方使用簡短握手恢復之前協商的會話。簡短握手因為不需要金鑰交換與金鑰生成等操作,所以會更快,並且少一次往返時間。

4.TLS的紀錄協議造成的在網路傳輸中的額外開銷

TLS協議的最小傳輸單位是一個TLS記錄,它最多可以包含2^14=16384位元組(16K)的資料。一條記錄在不加密的情況下只有很小的開銷;每個記錄以5位元組的元資料開頭,即內容型別(1位元組)、協議版本(2位元組)和資料長度(2位元組)。流加密、分組加密和已驗證密碼套件加密後的TLS記錄的額外開銷。

SSL/TLS深度解析--TLS效能優化

儘量避免傳送小包資料。儘量緩衝應用層資料避免額外的網路開銷。

5.對稱加密對CPU資源的消耗

加密操作有明顯的CPU成本,成本由加密演算法、加密模式和完整性校驗演算法三者決定。

6. TLS 記錄的快取延遲

TLS記錄是TLS傳送和接收資料的最小單位。TLS記錄的大小與下一層TCP包的大小並不匹配,一個全尺寸的TLS記錄16 KB需要被拆分成許多小的TCP包(大約12個),通常每個小於1.5 KB(1.3KB)。整個TLS記錄被分成小的TCP包後,各個小包會陸續到達,但在全部到齊之前是無法進行解密處理的。這是因為TLS記錄同樣是資料解密和完整性檢驗的最小單位。快取延遲有時可能會比較大。

雖然通過TCP協議可以把丟失和延遲的資料包恢復,但仍然需要消耗一次往返。每一次額外的往返對於整個TLS記錄都意味著延遲。 初始擁塞視窗另一個觸發額外往返的延遲是在連線初期傳送大量資料導致初始擁塞視窗溢位。一旦擁塞視窗滿了,傳送端必須等待響應(1次往返),等到擁塞視窗增加再發送更多資料。

如果Web伺服器支援TLS記錄調整,就應該考慮將預設值(16 KB這麼大的數值)改成更為合理的值,調整這個值由部署的密碼套件和相應的傳輸開銷決定,一般情況下設定成4 KB。如果將TLS記錄大小設定為與TCP/IP包準確匹配,那就設定成1400位元組左右,然後通過觀察資料包逐步調整。IP報文理論上最大是65535個位元組,是很大的,但是由於IP分片效果很不好,所以TCP在三次握手中互相得知對方的MSS(MTU減IP頭部),不給IP層很大塊的資料,避免IP資料報分片

例如,資料鏈路層最大傳輸單元(maximum transfer unit,MTU)是1500位元組,那麼可以預見:

1,500 bytes MTU 去除額外開銷,所傳資料 1379 —1419 bytes 。

-20 bytes IPv4 herder | - 40 bytes IPv6 header

- 32 bytes TCP header TCP 頭部 最小是20位元組可拓展的是40位元組,最大為60位元組

- 29 bytes TLS record | - 49 bytes TLS record


MSS 是1460 bytes :1460 - 32 - 29|49 = 1379 — 1399 bytes

首先MTU的值是變化的。雖然多數客戶端繼承乙太網1500位元組的限制,但也有一些協議支援更大的資料。比如,巨型幀(jumbo frame)允許多達9000位元組。還有就是使用IPv4和IPv6(IPv4頭是20位元組,IPv6頭是40位元組)計算會略有不同,所密碼套件的變化也會影響這個數值。

另一個問題是減小TLS記錄的大小會增加傳輸開銷,也就是吞吐量會下降。如果將TLS記錄長度調大(最大16K),那麼由於是加密的資料,得要所有的資料(所有的IP包)都到齊了,才會順利的解密出明文,等待的時間會較長,吞吐率是上去了,響應的實時性就下降了。nginx上也有配置這個值的選項,只是不能動態調整。