写点什么

SSL / TLS 协议解析!什么是 SNI? SNI 识别?

用户头像
明儿
关注
发布于: 2021 年 05 月 10 日
SSL / TLS协议解析!什么是SNI? SNI 识别?

自 Web 诞生以来,我们所接触的互联网时代,都有可能存在信息的截断,而 SSL 协议及其后代 TLS 提供了加密和安全性,使现代互联网安全成为可能。


这些协议已有将近二十多年的历史,其特点是不断更新,旨在与日趋复杂的攻击者保持同步。


什么是 SSL!什么是 TLS!


SSL 代表安全套接字层,该协议是由 Netscape 于 1990 年代中期开发的,Netscape 是当时最受欢迎的 Web 浏览器。SSL 1.0 从未向公众发布,而 SSL 2.0 具有严重的缺陷。1996 年发布的 SSL 3.0 进行了彻底的改进,为后续工作奠定了基础。


而 TLS 与 SSL,当一版本于 1999 年发布时,它由 Internet 工程任务组(IETF)进行了标准化,并被赋予了一个新名称:传输层安全性( TLS)。


正如 TLS 规范指出的那样,“此协议与 SSL 3.0 的区别并不明显。” 因此,TLS 和 SSL 并不是真正的问题。而是,这两者形成了一系列不断更新的协议,并且经常被合并为 SSL / TLS。


这样我们就明白了 TLS(传输层安全性)只是 SSL 的更新,更安全的版本。目前已经升级到 TLS1.3 版本,TLS 协议对所有类型的 Internet 通信进行加密。最常见的是网络流量;


简而言之,它们是保持 Internet 连接安全并保护两个系统之间发送的任何敏感数据的标准技术,可防止犯罪分子读取和修改所传输的任何信息,包括潜在的个人详细信息。


SSL/TLS 握手过程


握手过程非常复杂,并且协议允许有多种变体。以下步骤提供了一个大致的概述。


第一


客户端与服务器联系并请求安全连接。服务器以密码套件列表(即创建加密连接的算法工具包)答复



客户端将其与其自己的受支持密码套件列表进行比较,选择一个,然后让服务器知道它们都将使用它。



第二


服务器提供其数字证书,该数字证书是由第三方机构颁发的确认服务器身份的电子文档。包含服务器的公共加密密钥等等。



第三


客户端和服务器使用服务器的公共密钥,建立会话密钥,这两个会话密钥将用于会话的其余部分以加密通信。有几种技术可以做到这一点。


客户端可以使用公共密钥对随机数进行加密,然后将其发送到服务器进行解密,然后双方都可以使用该数字来建立会话密钥。


或者,两方可以使用所谓的 Diffie-Hellman 密钥交换来建立会话密钥。



TLS1.2 和 TLS1.3 比较


TLS1.2 它也允许使用较旧的加密技术,以支持较旧的计算机。不幸的是,这使它容易受到攻击,在中间人攻击中,黑客拦截了通信中的数据包,并在读取或更改数据包后将其发送出去。


幸运的是,TLS1.3 通过抛弃对旧加密系统的支持,填补了许多此类漏洞。使通信中的数据包受到了保护。


什么是 SNI


Server Name Indication (SNI) 是 TLS 协议(以前称为 SSL 协议)的扩展,该协议在 HTTPS 中使用。它包含在 TLS/SSL 握手流程中,以确保客户端设备能够看到他们尝试访问的网站的正确 SSL 证书。该扩展使得可以在 TLS 握手期间指定网站的主机名或域名 ,而不是在握手之后打开 HTTP 连接时指定。


SNI 的技术原理


SNI 通过让客户端发送虚拟域的名称作为 TLS 协商的 ClientHello 消息的一部分来解决此问题。这使服务器可以及早选择正确的虚拟域,并向浏览器提供包含正确名称的证书。



这样要说一下,服务器名称指示(SNI)有效负载未加密,因此客户端尝试连接的服务器的主机名对于被动的窃听者是可见的。


TLS 的 SNI 扩展有什么作用?


下面,我们就开始对 TLS 协议的 SNI 进行解析。


1、Web 服务器通常负责多个主机名–或域名。如果网站使用 HTTPS 则每个主机名将具有其自己的 SSL 证书。


2、在 HTTPS 中,先有 TLS 握手,然后才能开始 HTTP 对话。如果没有 SNI,客户端将无法向服务器指示正在与之通信的主机名。


3、如果服务器可能为错误的主机名生成 SSL 证书。那么 SSL 证书上的名称与客户端尝试访问的名称不匹配,则客户端浏览器将返回错误信息,并通常会终止连接。


4、通过 SNI,拥有多虚拟机主机和多域名的服务器就可以正常建立 TLS 连接了。


TLS 协议的 SNI 识别


这里给出的只是一部分代码实现。


static bool is_sslv3_or_tls(u_char *tls_data,int PayloadLen,int offset){    uint8_t   content_type = 0;    uint16_t  protocol_version = 0, record_length = 0;    SslSession        *session;

    if (PayloadLen < 5) {        return false;    }
    content_type = tls_data[offset];    offset += 1;    protocol_version = ntohs(*(uint16_t*)(tls_data + offset));    offset += 2;    record_length = ntohs(*(uint16_t*)(tls_data + offset));    //printf("record_length: %d\n",record_length);    /* These are the common types. */    if (content_type != SSL_ID_HANDSHAKE) {        return false;    }

    if (protocol_version != SSLV3_VERSION &&        protocol_version != TLSV1_VERSION &&        protocol_version != TLSV1DOT1_VERSION &&        protocol_version != TLSV1DOT2_VERSION) {        return false;    }
    if (record_length == 0 || record_length >= TLS_MAX_RECORD_LENGTH + 2048) {        return false;    }
    return true;}

static void dissect_ssl2_hnd_client_hello(u_char *tls_data,int PayloadLen, int offset){    uint32_t offset_end = 0;    uint16_t ext_type = 0;    uint32_t ext_len = 0;    uint32_t next_offset = 0;    int    msg_length = 0;    int    remaining_length = 0;    uint32_t server_name_length = 0;    uint16_t length = 0;    uint16_t version = 0;    int session_id_len = 0;    uint32_t extensions_length = 0;    uint8_t random[32];     char s_name[256] = {0};    int i = 0;      uint32_t     cipher_suite_length = 0;    offset += 3;/*Length*/
    version = ntohs(*(uint16_t*)(tls_data + offset));    printf("Version: 0x%.2X\n",version);    offset += 2; /*Version*/
    if (offset > PayloadLen)        return;    memcpy(random,tls_data + offset,32);
    for (i = 0; i < 32;i++)        printf("%x",random[i]);    printf("\n");
    offset += 32; /*random*/
    session_id_len = tls_data[offset];    printf("session_id_len: %d\n",session_id_len);    offset += 1; /*Session ID Length*/    if (session_id_len > PayloadLen)        return;    uint8_t session_id[session_id_len];    memcpy(session_id,tls_data + offset,session_id_len);
    for (i = 0; i < session_id_len;i++)        printf("%x",session_id[i]);    printf("\n");
    offset += session_id_len; /*Session ID */
    cipher_suite_length = ntohs(*(uint16_t*)(tls_data + offset));       printf("cipher_suite_length: %d\n",cipher_suite_length);
    offset += 2;/*cipher_suite_length*/
    offset += cipher_suite_length; /*Session ID */
    offset += 1;/*compression_method length*/
    offset += 1;/*compression_method */    extensions_length = ntohs(*(uint16_t*)(tls_data + offset));    printf("Extensions Length: %d\n",extensions_length);    printf("Extensions Length: 0x%.2X\n",extensions_length);    offset += 2;    //offset_end = PayloadLen - offset;    //printf("offset_end: %d\n",offset_end);    msg_length = PayloadLen;    while (offset < msg_length)    {        /* Get the type and length */        remaining_length = msg_length - offset;        if (remaining_length < 3) {            printf("Not enough data left for IE and length, %i bytes\n", remaining_length);            return;        }
        ext_type = ntohs(*(uint16_t*)(tls_data + offset));        //printf("ext_type: 0x%.2X\n",ext_type);        offset += 2;        ext_len  = ntohs(*(uint16_t*)(tls_data + offset));        //printf("ext_len: %d\n",ext_len);        offset += 2;
        switch (ext_type)         {            case SSL_HND_HELLO_EXT_SERVER_NAME:                 printf("==========SERVER_NAME======\n");
                 offset += 2; //Server Name list length
                 offset += 1; //Server Name Type                 server_name_length = ntohs(*(uint16_t*)(tls_data + offset));                 printf("server_name_length: %d\n",server_name_length);                 offset += 2; //Server Name length
                 memset(s_name,0,256);                 memcpy(s_name,tls_data + offset,server_name_length);                 printf("Server Name: %s\n",s_name);

            break;            default:            break;        }
       offset += ext_len;
    }

}

void dissect_tls(u_char *tls_data,int PayloadLen,int offset){    SslSession   *session;    uint16_t version = 0;    uint16_t length = 0;    uint8_t  msg_type = 0;    printf("0x%.2X,0x%.2X,0x%.2X 0x%.2X,0x%.2X,0x%.2X \n",tls_data[offset],tls_data[offset+1],tls_data[offset+2],tls_data[offset+3],tls_data[offset+4],tls_data[offset+5]);
    if (!is_sslv3_or_tls(tls_data,PayloadLen,offset)) {        return;    }
    printf("====tls=========\n");    offset += 1; /*Content Type*/    version = ntohs(*(uint16_t*)(tls_data + offset));       printf("Version: 0x%.2X\n",version);        offset += 2; /*Version*/
    length = ntohs(*(uint16_t*)(tls_data + offset));    printf("Length: %d\n",length);    offset += 2; /*length*/    /* fetch the msg_type */    msg_type = tls_data[offset];     offset += 1; /*handshake type*/    printf("handshake type: 0x%.2X\n",msg_type);        switch (version)     {        case SSLV3_VERSION:        case TLSV1_VERSION:        case TLSV1DOT1_VERSION:        case TLSV1DOT2_VERSION:            if (PayloadLen < 5)             {                break;            }            ssl_looks_like_valid_v2_handshake(tls_data,PayloadLen,msg_type,offset);        break;        default:        if (PayloadLen < 5)         {            break;        }    }
}
复制代码


编译运行:



在客户端到服务端方向的 ClientHello 包中,提取 SNI 扩展字段中的 SNI_NAME 字段,通过数据包偏移量进行逐步匹配得到 SNI_NAME。对 SNI_NAME 进行规则匹配。


总结


这篇文章只是对 SSL/TLS 做一些简单的介绍,想通过更多认识,可以阅读官方的 RFC 文档,对于 SNI(Server Name Indication)是 TLS 的扩展,它主要是用来解决一个服务器拥有多个域名的情况。通过 SNI,拥有多虚拟机主机和多域名的服务器就可以正常建立 TLS 连接了。


欢迎关注微信公众号【程序猿编码】,欢迎添加本人微信号(17865354792),交个朋友,咱们一起学习进步!

发布于: 2021 年 05 月 10 日阅读数: 31
用户头像

明儿

关注

专注分享Linux c/c++ 、Python、Go语言。 2021.04.19 加入

彩虹风雨后,成功细节中。

评论

发布
暂无评论
SSL / TLS协议解析!什么是SNI? SNI 识别?