[tls][https][nginx] https的client session cache與session ticket機制分析


more title

tls的客戶端會話恢復與會話票證機制分析

golang fasthttp庫關於會話恢復與會話票證的源碼分析

 

前言

https握一次手是很艱辛的,計算量很大。所以如果連續兩次短連接通信的話,完全可以

復用上一次的會話。這樣可以壓縮通信,節省計算。

TLS提供了兩個機制來做這個事。分別是

session cache(會話緩存,會話恢復)

session ticket (會話票證)

此二者,除了達到的結果一樣以為,在機制上並沒有什么其他關系。

 [classic_tong @ https://www.cnblogs.com/hugetong/p/12192587.html]

 

結果一致

先來看,成功的恢復了上一次的會話,或者說成功復用的情況是啥樣的。

在server發給client的包里,如下截圖中體現的特征,可以說明,兩端支持了session cache或者session ticket:

 

 

Session Cache分析

nginx可以通過如下配置,打開session cache

Syntax:    ssl_session_cache off | none | [builtin[:size]] [shared:name:size];
Default:    
ssl_session_cache none;
Context:    http, server

見:https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_cache

開session cache而不開session ticket的client模擬,這個事用瀏覽器,curl,wget都不是很好模擬。筆者千辛萬苦終於發現openssl

准備了這個場景,構建如下:

openssl s_client -connect test1.www.local:443 --reconnect -no_ticket -CAfile ~/Keys/https/root/root.cer

主要是reconnect參數,他會連發5次,專門用來測session cache。

 

協議細節

這里有個關鍵信息,叫session id,用來代表tls會話。分別位於client hello與server hello里。二者相同時,說明通過協商恢復了舊的session。

二者不同時,server hello中ID,作為本次會話的代表。(如下圖,是我從實驗中的首次TLS會話的serverhello)

當client渴望恢復會話時,它會把本地保存的上次的會話ID在 client hello中發給server。(如下圖,為緊接着上圖的第二次TLS會話)

 

當server收到了session id非空的clienthello時,會知道client希望進行會話恢復。如果他在本地找到了這個會話,便會用相同的ID進行

serverhello回復,否則便生成新的會話,自然也使用新的session id。(如下圖,是成功進行了會話恢復的情況)

 [classic_tong @ https://www.cnblogs.com/hugetong/p/12192587.html]

 

Session Ticket分析

session ticket相比於前者,是一種新的會話恢復機制。它的思想在於服務器去處它的所有會話數據(狀態)並進行加密,再以票證的方式發回

客戶端。在接下來的連接中,客戶端將票證提交回服務器,由服務器檢查票證的完整性,解密其內容,再使用其中的信息恢復會哈。這種方式

有可能使擴展服務器集群更為簡單,因為如果不使用這種方式,就需要在服務集群的各個節點之間同步會話。(摘自<https權威指南>)

不過,需要額外提及的是。session ticket的引入,破壞了TLS的安全模型。(略)

 

配置

nginx的配置方法。默認就是開的,其實不用配

Syntax:    ssl_session_tickets on | off;
Default:    
ssl_session_tickets on;
Context:    http, server
This directive appeared in version 1.5.9.

client,我用的是筆者fork的一個github項目,gobench:https://github.com/tony-caotong/gobench

它背后使用的是golang的fasthttp庫,后面會祥述。https://github.com/valyala/fasthttp

 

協議細節

RFC:https://tools.ietf.org/html/rfc5077

 

client

當client支持session ticket的時候,在client hello中會包含一個叫做session_ticket的擴展字段。當本地沒有需要恢復的ticket,但是渴望與server

達成一致對ticket進行支持時,如圖。

 

 當本地,有需要恢復的ticket時,session_ticket 字段會存這需要恢復的ticket。如圖:

 

 當client不支持session ticket時,client hello中,將不包括session ticket數據段。

 

server

server收到ticket的請求后,如果是一個空的ticket字段。server也會回復一個空的session ticket字段,表明它

即將發起一個新的session ticket握手,並隨后發送NewSessionTIcket報文。

 

 

about session id

ticker啟用的前提下,client會在client hello里會帶上一個session id。如果serverhello回復了同樣的session

id,說明server接受了這個ticket,並且同樣恢復。如果serverhello回復了一個新的session id。說明server

拒絕了client的ticket,並將發起一個新的ticket。

也是就是說,成功恢復的鏈接,client與server的兩個session id相同。

 

 Server是怎么解密的呢

前文的rfc5077第一段就講了。Server用一個只有他知道的key,把server的state(以及key?)加密了作為ticket的內容發給client。

client其實也解不開,也不知道里邊是啥。它只能原樣發回來,server用它自己的key解密。這樣他就不用為每個鏈接保存state了,

有點類似於tcp的那個ticket。見多了就發現了,思路都是一樣的。

 

見nginx的配置文件,可以更好的理解:

https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_ticket_key

他可以顯示的配置一個key,這樣的話,多個server就可以共享ticket了。或者不配置就使用一個隨機生成的key。

 

more

關於gobench,關於fasthttp,關於golang tls

之所以分析了以上內容與機制,主要是因為fasthttp(golang tls)的實現中,將ticket與cache的耦合在了一起。

golang的api不能配置出一種場景:ticket關,但是cache開。(也可能不是這一點,是我不會用,其實我幾乎不會golang)所幸用上文的openssl命令可以彌補。

當關閉ticket的時候,cache功能也失效了。

主要因為在這個函數里:crypto/tls/handshake_client.go::loadSession(),有這樣的一行判斷。

 

[classic_tong @ https://www.cnblogs.com/hugetong/p/12192587.html]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM