一、什么是重协商
大部分TLS连接都以handshake为开始,经过应用数据的交换,最后关闭会话。如果在第一次handshake之后(可能经历了应用数据的交换也可能没有)请求重新协商,就会发起一次新的handshake,对新的安全参数达成一致。重协商的handshake的消息都是全部加密的,这与第一次handshake明显不同。
重协商功能应用场景举例:
*) Client证书:可以设置访问网站的根路径不要求client携带证书,而在client访问特定子区域时server发起重协商请求,要求client携带证书;
*) 隐藏消息:由于重协商的handshake消息是加密的,被动攻击者无法监视协商过程,这样就可以隐藏一些敏感信息(比如证书中包含的身份识别信息)。
二、怎样发起重协商
有两种方式可以发起重协商:
*)Client发起:TLS协议允许client在任意时间简单地发送新的ClientHello消息请求重新协商,就如同建立一个新的连接一样;
*)Server发起:如果server希望重新协商,它会发送HelloRequest消息给client,这个消息通知client停止发送应用数据,并开始新的handshake。
三、重协商的安全性
重协商机制并不安全,针对重协商的攻击类型如下:
3.1 DoS攻击
TLS的handshake过程需要使用非对称算法进行身份认证和密钥协商,这个过程需要很多计算资源。Handshake本来只在TLS连接开始建立时执行一次,但由于重协商机制的引入,使得client被允许不断发起新的handshake。由于client可以使用较少的资源来执行handshake(比如:不检查server的证书,这样可以避免校验签名的开销),这样攻击者就可以更容易地耗尽server的资源导致其拒绝为其它用户的请求提供服务。
这种攻击与分布式拒绝服务攻击(DDoS)的不同之处在于,它不需要大量的攻击来消耗网络带宽,而仅仅通过一台主机的一个TCP/IP socket来耗尽server的资源(这样就会导致当前的DoS和DDoS防御策略无效)。例如,一台server通常能执行150-300次/s握手,而一个client可以发起多达1000次/s握手请求。
防御方法:
1) 禁用重协商功能:不推荐,因为这样会导致依赖重协商的特性无法使用;
2) 禁止client发起重协商:目前看来似乎是个不错的选择;
3) 速率限制:对新到来的TLS连接和重协商的速率进行限制;
4) 使用SSL加速卡:通过极大地提高server对handshake的处理能力来增加攻击的成本,但可能攻击者只增加一到两台主机进行攻击就可以使得此措施无效。
3.2 中间人攻击
由于TLS的重协商前后的两条TLS连接之间没有关联(即使它们发生在同一条TCP连接上),而且应用层(如HTTP)与加密层很少交互(例如,如果重协商发生在HTTP请求的过程中,上层应用是得不到通知的),导致TLS层面发生的事情与上层应用了解到的信息不匹配。
因此,一个中间人(man-in-the-middle,MITM)攻击者就可以通过如下步骤来利用这个漏洞:
1) 拦截一个client到server的TCP连接,截住其TLS handshake请求;
2) 新建一个到server的TLS连接,在handshake之后发送攻击负载;
3) 将1)中拦截的handshake请求通过与server的TLS连接发送过去,这样在server看来是重协商,而在client看来是一条全新的TLS连接。一旦重协商完成,client与server开始交换应用层数据,攻击者的攻击负载和client的正常数据就会被server合并处理,从而使得攻击成功。
攻击过程(举例)的示意图如下:
这种攻击会使得server执行攻击者制定的任意GET请求。
对于这种MITM攻击,即使禁止了client发起重协商,依赖于client证书校验和支持SGC的网站仍然容易遭到攻击。因为攻击者只需要调查网站在哪些情况下是需要进行重协商的,如果条件得到满足则攻击者就可以开展攻击行为。
防御方法:
1)禁用重协商功能:不推荐,除了会使得依赖重协商的特性无法使用外,还会导致增加了网络上重协商功能的不确定性,使得client无法有效保护自己【注1】
2)使用“安全重协商”功能:通过关联重协商前后的TLS连接来阻止非法数据注入;详见第四节。
【注1】:重协商的安全缺陷对client的威胁在于:攻击者可以通过控制服务器来攻击与之通信的client。由于在攻击发生时client并未参与到重协商的过程中,故对于client唯一可行的保护自己的方法就是只于支持安全重协商的server建立连接。对于禁用了重协商功能的server,client不希望自己无法连接它们,但client无法区分server是禁用了重协商还是不支持安全重协商。所以server禁用重协商的行为会导致client很难使用有效的方法来保护自己。
四、安全重协商
为了解决中间人攻击的问题,【RFC5764】提出了“安全重协商”机制。本质很简单,就是关联两次握手,方式是提供了一个新的扩展(renegotiation_info)。SSLv3/TLS 1.0不支持扩展,为了使其支持安全重协商,client需要发送TLS_EMPTY_RENEGOTIATION_INFO_SCSV(0xFF)密码套件(缩写为SCSV)。
安全重协商的流程如下:
1) 在某个连接的第一次握手期间,双方通过renegotiation_info扩展或SCSV套件通知对方自己支持安全重协商;
2) 在handshake过程中,client和server都分别记录Finish消息之中的client_verify_data和server_verify_data;
3)重协商时client在ClientHello中包含client_verify_data,server在ServerHello中包含client_verify_data和server_verify_data。对于受害者,如果协商中不会携带这些数据则连接无法建立。由于Finished消息总是加密的,攻击者无法得到client_verify_data和server_verify_data的值。
五、OpenSSL中的重协商(基于OpenSSL-1.1.0f)
5.1 发起重协商
5.1.1 SSL_renegotiate
Client和server只需调用SSL_renegotiate(ssl)函数即可完成发起重协商的设置。SSL_renegotiate()函数定义如下:
1641 int SSL_renegotiate(SSL *s)
1642 {
1643 if (s->renegotiate == 0)
1644 s->renegotiate = 1;
1645
1646 s->new_session = 1;
1647
1648 return (s->method->ssl_renegotiate(s));
1649 }
对于TLS_client_method()和TLS_server_method(),s->method->ssl_renegotiate指向ssl3_renegotiate():
3865 int ssl3_renegotiate(SSL *s)
3866 {
3867 if (s->handshake_func == NULL)
3868 return (1);
3869
3870 if (s->s3->flags &SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS)
3871 return (0);
3872
3873 s->s3->renegotiate = 1;
3874 return (1);
3875 }
可见,SSL_renegotiate()函数只是将s->s3->renegotiate设置为1而已,并不是发送重协商报文(Handshake,HelloRequest)。发送重协商报文是在SSL_write()或SSL_read()函数被调用的时候进行的:
5.1.2 发送第一个消息
先来看SSL_write()函数。对于TLS_client_method()和TLS_server_method(),SSL_write()最终都会调用ssl3_write()函数:
3816 int ssl3_write(SSL *s, const void *buf, int len)
3817 {
3818 clear_sys_error();
3819 if (s->s3->renegotiate)
3820 ssl3_renegotiate_check(s);
3821
3822 return s->method->ssl_write_bytes(s,SSL3_RT_APPLICATION_DATA, buf, len);
3823 }
由于之前调用的SSL_renegotiate()函数将s->s3->renegotiate设置为1,故会在3820行调用到ssl3_renegotiate_check()函数:
3877 int ssl3_renegotiate_check(SSL *s)
3878 {
3879 int ret = 0;
3880
3881 if (s->s3->renegotiate) {
3882 if(!RECORD_LAYER_read_pending(&s->rlayer)
3883 &&!RECORD_LAYER_write_pending(&s->rlayer)
3884 && !SSL_in_init(s)) {
3885 /*
3886 * if we are the server, and wehave sent a 'RENEGOTIATE'
3887 * message, we need to set thestate machine into the renegotiate
3888 * state.
3889 */
3890 ossl_statem_set_renegotiate(s);
3891 s->s3->renegotiate = 0;
3892 s->s3->num_renegotiations++;
3893 s->s3->total_renegotiations++;
3894 ret = 1;
3895 }
3896 }
3897 return (ret);
3898 }
其中的关键代码是3890行ossl_statem_set_renegotiate()函数:
103 /*
104 * Set the state machine up ready for arenegotiation handshake
105 */
106 void ossl_statem_set_renegotiate(SSL *s)
107 {
108 s->statem.state = MSG_FLOW_RENEGOTIATE;
109 s->statem.in_init = 1;
110 }
调用完ssl3_renegotiate_check()函数之后,ssl3_write()会调用s->method->ssl_write_bytes指向的ssl3_write_bytes()函数:
343 int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len)
344 {
…
379 if (SSL_in_init(s) &&!ossl_statem_get_in_handshake(s)) {
380 i = s->handshake_func(s);
381 if (i < 0)
382 return (i);
383 if (i == 0) {
384 SSLerr(SSL_F_SSL3_WRITE_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
385 return -1;
386 }
387 }
…
其中SSL_in_init(s)的返回值会是1:
69 int SSL_in_init(SSL *s)
70 {
71 return s->statem.in_init;
72 }
由于是在handshake结束之后调用,故ossl_statem_get_in_handshake(s)的返回值会是0:
141 int ossl_statem_get_in_handshake(SSL *s)
142 {
143 return s->statem.in_handshake;
144 }
故ssl3_write_bytes()会执行380行s->handshake_func(s)。
再来看SSL_read()。对于TLS_client_method()和TLS_server_method(),这个函数最终会调用ssl3_read():
3825 static int ssl3_read_internal(SSL *s, void *buf, int len, int peek)
3826 {
3827 int ret;
3828
3829 clear_sys_error();
3830 if (s->s3->renegotiate)
3831 ssl3_renegotiate_check(s);
3832 s->s3->in_read_app_data = 1;
3833 ret =
3834 s->method->ssl_read_bytes(s,SSL3_RT_APPLICATION_DATA, NULL, buf, len,
3835 peek);
3836 if ((ret == -1) &&(s->s3->in_read_app_data == 2)) {
3837 /*
3838 * ssl3_read_bytes decided to calls->handshake_func, which called
3839 * ssl3_read_bytes to read handshakedata. However, ssl3_read_bytes
3840 * actually found application data andthinks that application data
3841 * makes sense here; so disablehandshake processing and try to read
3842 * application data again.
3843 */
3844 ossl_statem_set_in_handshake(s, 1);
3845 ret =
3846 s->method->ssl_read_bytes(s,SSL3_RT_APPLICATION_DATA, NULL, buf,
3847 len,peek);
3848 ossl_statem_set_in_handshake(s, 0);
3849 } else
3850 s->s3->in_read_app_data = 0;
3851
3852 return (ret);
3853 }
3854
3855 int ssl3_read(SSL *s, void *buf, int len)
3856 {
3857 return ssl3_read_internal(s, buf, len, 0);
3858 }
调用SSL_renegotiate()后3831行会被执行,其影响见上文对SSL_write()函数的分析。s->method->ssl_read_bytes()指向ssl3_read_bytes():
975 int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
976 int len, int peek)
977 {
…
1029 if (!ossl_statem_get_in_handshake(s)&& SSL_in_init(s)) {
1030 /* type == SSL3_RT_APPLICATION_DATA */
1031 i = s->handshake_func(s);
1032 if (i < 0)
1033 return (i);
1034 if (i == 0) {
1035 SSLerr(SSL_F_SSL3_READ_BYTES,SSL_R_SSL_HANDSHAKE_FAILURE);
1036 return (-1);
1037 }
1038 }
…
最后SSL_read()会执行1031行代码。可见在调用SSL_renegotiate()开启协商功能后,SSL_write()和SSL_read()都会调用s->handshake_func(s),对于client会调用到ossl_statem_connect:
168 int ossl_statem_connect(SSL *s)
169 {
170 return state_machine(s, 0);
171 }
对于server则会调用ossl_statem_accept():