1. 重协商(Renegotiation)
就是在现有的 TLS 连接之上,重新进行 TLS 握手过程,以产生全新的 TLS 加密参数的过程。
服务器和客户都可以发起这个重协商过程(在任何时刻)。
客户端: 可以在任意时刻发送新的 ClientHello
消息来出发重协商过程。
服务器:当服务器端想发起重协商的过程,它需要给客户端发送一个 HelloRequest
的请求,客户端在收到这个消息后可以根据情况决定是否开始重协商。
重协商的握手和初始的 TLS 握手流程相同,不同的是在重协商时,服务器和客户端已经有了可以用于加密通信数据的加密参数,因此,此时发送的握手TLS数据包都是加密的。
2. 为什么需要安全的重协商扩展?
TLS 协议中允许通信双方(服务器或者客户端)发起重协商来建立一套新的加密参数。 但是,由于缺乏必要的信息来将重协商前后的上下文关联起来,这里就存在中间人攻击的安全隐患。
中间人攻击示意图:
Client Attacker Server
1. ------- TCP CONNECT ----------->
2. ----------- TCP CONN && TLS CONN ---->
3. ----------- Send Attack App Data ---->
4. ----------- TLS Handshake ----->
5. ----------- TLS Renegotiation -------->
6. ------------- Send App Data ----->
7. ----------- Send App Data ------------>
- 首先,攻击者(Attacker) 会伪装成客户端(Client) 想要访问的服务器(Server), 欺骗客户端和自己建立一条 TCP 连接。 此时,仅仅一条 TCP 连接,还没有开始 TLS 握手。
- 紧接着, 攻击者将自己伪装成客户端和真正的服务器之间建立一条 TLS 连接。
- 在攻击者和服务器建立 TLS 连接之后,和 客户端建立 TLS 连接之前,发送一段攻击数据。
- 客户端尝试和伪装成服务器的攻击者进行 TLS 握手,建立一条 TSL 连接.
- 攻击者和客户端建立 TLS 连接之后,根据客户端的加密参数来向服务器端发起 TLS 重协商的请求。
- 从此之后,攻击者作为中间人,只需要转发来自客户端的 TCP 数据就行。
这个流程中,出问题的地方在于, 第3步中攻击者发送的攻击数据会被当服务器当作来自客户端的数据和第6步中的数据合并处理。
举个例子:
Attack App Data:
'THIS IS A ATTACK DATA'
App Data(from the client, step6):
'THIS IS A REAL USER DATA'
The App Data the server side received will be:
'THIS IS A ATTACK DATATHIS IS A REAL USER DATA'
这里,服务器无法识别出攻击数据, 而把它作为正常的应用程数据传递给上层引用来处理, 从而因此安全隐患。
3. 安全重协商
RFC5746 中详细的介绍了安全重协商的实现。
整个流程大体分为五个方面:
1. 客户端发送 CLIENT_HELLO 以和服务器端建立一条 TLS 连接
2. 服务器收到 CLIENT_HELLO 后回复 SERVER_HELLO
3. 服务端发送 HelloRequest 要求客户端进行重协商
4. 客户端发起重协商,再次发送 CLIENT_HELLO
5. 服务器端回复相应的 SERVER_HELLO.
3.1 客户端发送初始 CLIENT_HELLO
这里有两种方法来向服务器端表明我们支持安全的重协商:
-
通过在 CLIENT_HELLO 中包含
renegotiation_info
扩展。 因为是初次 TLS 握手,这个扩展的扩展数据部分长度为零。 -
通过在 CLIENT_HELLO 中包含一个特殊的
Cipher Value: TLS_EMPTY_RENEGOTIATION_INFO_SCSV(0x00ff)
。
SCSV 这种方式是为了兼容 SSLv3 ,老的实现在遇到不支持的 TLS 扩展的时候可能会断掉 TCP 连接,而对于无法识别的 Cipher 值则会忽略。 因此这种方式是向后兼容的。
这两种方式二选一即可。
3.2 服务器端回复 SERVER_HELLO
服务器收到客户端的 CLIETN_HELLO 之后,查看是否包含上述两种信息。 如果包含,则回复 SERVER_HELLO,并携带 renegotiation_info
扩展, 扩展数据部分依然是空。
紧接着的其他 TLS 握手流程相同。
在交换了 CLIENT_HELLO 和 SERVER_HELLO 之后,互相知道对方支持安全的重协商,那么就可以在接下来的时间里,选择进行重协商了。否则,不能进行重协商。
3.3 服务端发送 HelloRequest 要求客户端进行重协商
服务器通过发送 HelloRequest 的方式来提示客户端进行重协商。 客户端可以选择忽略掉这个请求。
3.4 客户端发起重协商,再次发送 CLIENT_HELLO
当客户端决定进行重协商时,需要重新发送 CLIENT_HELLO 请求。
这里不同于 3.1 的是,必须使用 renegotiation_info
扩展了,并且这个扩展中不再为空。
扩展数据部分是 client_verify_data: 客户端在上一次TLS握手过程中发送的 Finished 消息中的 verify_data 的数据。
3.5 服务器端回复相应的 SERVER_HELLO.
服务器端收到响应的 CLIENT_HELLO 之后,需要校验 renegotiation_info
扩展中的数据正确有效。 如果校验通过,则需要回复 SERVER_HELLO,同时也携带renegotiation_info
扩展。
扩展数据部分是 client_verify_data 和 server_verify_data
客户端在收到这个 SERVER_HELLO 之后,也同样需要校验其中的数据是否有效。
如果真实有效,紧接着的其他 TLS 握手流程相同。握手结束之后,就拥有了一套全新的加密参数。
4. 使用 OpenSSL 测试重协商
openssl s_client -connect xxx.xxx.xxx.xxx:xxxxx
// 输出中包含:
Secure Renegotiation IS supported
在命令行中输入 R, 然后回车
RENEGOTIATING