TLSrenegotiation
在TLS安全重协商出来前,有一个TLS重协商的漏洞,它会被中间人攻击利用,可以插入非法数据到客户端和服务器的安全连接中。虽然这个漏洞无法被用来破解密钥,但是可以用来插入恶意数据,比如在HTTPS中插入恶意的JavaScript代码。该漏洞在2009年的时候被发现,但是直到2011年才被修复,修复的方案就是本文要介绍的“安全重协商”。
重协商的有两种方式:
ClientHello消息去示意服务器开启重协商,如果服务器同意的话就会回复ServerHello开启新的握手,这个过程是可以无限重复的。HelloRequest消息去示意客户端开启重协商,如果客户端同意的话就会回复ClientHello开启新的握手,这个过程是可以无限重复的。重协商的漏洞的根本原因是,重协商的消息没有跟已经建立的TLS连接绑定,因此服务器无法鉴别ClinetHello是否来自真实客户端的,会误以为是客户端发起的重协商,从而导致中间人攻击。其攻击过程大致如下:
重协商漏洞
那么引入的“安全重协商”是如何解决该问题的呢?我们前面讲到,根本问题是服务器无法鉴别ClientHello是否来自同一个客户端,即没有做到绑定两次协商,因此解决方案就是完成绑定,具体绑定方法可以从下面安全重协商过程中得到答案:
安全重协商
如上所示,在ClientHello和ServerHello中引入了新的扩展renegotiation_info。客户端如果支持安全重协商就在扩展中携带renegotiation_info扩展,而服务器如果接受安全重协商就在ServerHello扩展中携带renegotiation_info扩展以作为回应,当然如果服务器不想支持的话就不需要携带,这样客户端就知道服务器不想支持了。
在初始协商即Hello的过程中,该扩展的内容应该为空,即数据大小为0,否则应当立刻返回fatal_alert。细心的同学应该看到了,在ClientHello中还有一个“CSCV(Signaling Cipher Suite Value)”的值,实际的值是TLS_EMPTY_RENEGOTIATION_INFO_SCSV,它是一个信令套件,主要作用是:
SSLv3和TLS1.0,这些协议版本不支持扩展,因此无法携带renegotiation_info扩展,但是它们发送信令套件。SSLv2可看 Appendix E.2 of [RFC5246]虽然没有规定客户端必须用哪一种方式,甚至可以一起用,但是建议是客户端使用“CSCV”,因为兼容性更好。
在Hello交互后,双方都应该记录重协商的状态,即如果支持则renegotiation_flag=1,反之renegotiation_flag=0。
接下来会继续完成剩余的握手过程,区别是如果renegotiation_flag=1的话,客户端和服务器双方都必须记录下来自Finished消息的client_verify_data和server_verify_data,以便下次重协商时使用。
当客户端准备发起重协商时,它会在ClientHello中携带renegotiation_info扩展,值为client_verify_data,服务器收到后必须确保:
TLS_EMPTY_RENEGOTIATION_INFO_SCSV,如果有,应该返回一个fatal_alert。renegotiation_info扩展,且值为client_verify_data。这里我们可以看到,重协商跟上一个TLS会话做了绑定,因此服务器可以鉴别ClientHello是否来自同一个客户端,从而防止中间人攻击。
如果服务器接收重协商,那么服务器发送ServerHello,包含renegotiation_info扩展,值包含server_verify_data和client_verify_data。
客户端收到了新的ServerHello并核对renegotiation_info扩展的值是否正确(跟自己记录的server_verify_data和client_verify_data做比对,看是否相等),这里也可以看到,重协商跟上一个TLS会话绑定,因此客户端可以鉴别ServerHello是否来自同一个服务器,并且可以知道服务器是否有受到攻击,从而防止中间人攻击。
最后正常进行后续握手,在握手完成后客户端和服务器必须保存新的client_verify_data和server_verify_data,以便下次重新协商时使用。
有了这样的绑定,中间人将无处可破:
安全重协商攻击
新增了三种连接状态值:
secure_renegotiation flag(上文中提到的renegotiation_flag)client_verify_data:来自客户端的 Finished 消息server_verify_data:来自服务器的 Finished 消息新增扩展类型renegotiation_info,其值为0xff01 新增的信令套件TLS_EMPTY_RENEGOTIATION_INFO_SCSV,其值为{0x00, 0xFF}
数据结构为:
struct {
// renegotiated_connection_length
opaque renegotiated_connection<0..255>;
} RenegotiationInfo;扩展的值为:
ClientHello和ServerHello中,它们的值都是0长度的,因此它的值固定为ff 01 00 01 00:
ClientHellos,那么它会包含client_verify_data
ServerHellos,那么它会包含client_verify_data和server_verify_data。对于当前版本,这个是24字节值,对于SSLv3,它是一个72字节值。它可以跟DTLS [RFC4347]一起使用。
总结协商步骤:
ClientHello,包含renegotiation_info扩展或信令套件TLS_EMPTY_RENEGOTIATION_INFO_SCSV,如果是renegotiation_info扩展,值应该是0长度的。renegotiation_info或TLS_EMPTY_RENEGOTIATION_INFO_SCSV后,如果接受重新协商,应当返回一个renegotiation_info扩展,值应该是0长度的。并且设置secure_renegotiation为TRUEreneogtiation_info扩展,那么应该判断它是否是0长度,否则返回一个fatal_alert。ClientHello,包含renegotiation_info扩展,值为client_verify_data。ClientHello后,必须确保:TLS_EMPTY_RENEGOTIATION_INFO_SCSV,如果有,应该返回一个fatal_alert。renegotiation_info扩展,且值为client_verify_data。ServerHello,包含renegotiation_info扩展,值包含server_verify_data和client_verify_data。ServerHello并核对renegotiation_info扩展的值是否正确(server_verify_data和client_verify_data)client_verify_data和server_verify_data,以便下次重新协商时使用。(非安全)重协商处置方式:
HelloRequest后,在新的ClientHello中应该包含renegotiation_info扩展或TLS_EMPTY_RENEGOTIATION_INFO_SCSV。ClientHello必须确保没有TLS_EMPTY_RENEGOTIATION_INFO_SCSV,如果有就必须返回一个fatal_alert。renegotiation_info扩展,如果没有就必须返回一个fatal_alert。OPENSSL相关选项:
SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATIONSSL_OP_NO_RENEGOTIATION