[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]

posted on 2020-01-14 18:38  toong  阅读(4933)  评论(0编辑  收藏  举报