OpenSSL-TLS重协商

本文深入探讨了TLS连接中的重协商过程,包括重协商的定义、发起方式及安全问题。针对DoS攻击和中间人攻击,讨论了防御策略。重点介绍了OpenSSL 1.1.0f中的重协商实现,包括安全重协商功能和配置策略,强调了安全性和配置选项的重要性。
摘要由CSDN通过智能技术生成

一、什么是重协商

    大部分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():

                
  • 15
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值