Server Name Indication(SNI),HTTP/TLS握手过程解析

Server Name Indication(SNI)是一种TLS扩展,用于在TLS握手过程中传递服务器的域名信息。在未使用SNI之前,客户端在建立TLS连接时只能发送单个IP地址,并且服务器无法知道客户端请求的具体域名。这导致服务器需要使用默认证书进行握手,无法正确选择合适的证书。

使用SNI扩展后,客户端在发送ClientHello消息时会包含所请求的服务器的域名。服务器根据该域名来选择对应的证书进行握手,从而实现了多个域名共享同一个IP地址并使用不同证书的能力。

SNI对于虚拟主机或者CDN等场景特别有用,因为这些场景下,多个网站可能共享同一个IP地址。通过使用SNI,服务器能够正确地选择与域名匹配的证书,提高了安全性和灵活性。

SNI产生解决什么问题

Server Name Indication(SNI)的产生背景与互联网发展和IPv4地址短缺有关。

在互联网发展初期,每个网站通常拥有自己独立的IP地址。但随着网站数量的迅速增长和IPv4地址资源的有限,IP地址短缺问题变得日益严重。

为了解决IPv4地址短缺问题,一种解决方案是引入虚拟主机技术。虚拟主机是指在同一台物理服务器上托管多个不同域名的网站。通过共享同一个IP地址,多个网站能够减少IP地址的使用,并降低运维成本。

然而,这种虚拟主机技术在使用TLS(Transport Layer Security)协议时遇到了问题。TLS协议是用于加密和保护网络通信的协议,它在握手过程中需要使用正确的证书来验证服务器身份。但在使用虚拟主机时,服务器无法根据传统的握手方式正确选择合适的证书,因为无法获知客户端请求的具体域名。

为了解决这个问题,SNI扩展被引入TLS协议中。它允许客户端在握手时发送所请求的服务器域名信息,使服务器能够正确选择并使用对应的证书,实现了多个域名共享同一个IP地址并使用不同证书的能力。SNI的引入为虚拟主机和CDN等场景提供了更好的支持,进一步促进了IPv4地址资源的有效利用。

抓包分析http握手过程

触发http请求

curl www.baidu.com

tcpdump抓包:

tcpdump -i any port 80 -nnnvvvv -w http.pcap


HTTP (Hypertext Transfer Protocol) 的握手过程如下:

  • 1.建立TCP连接:客户端通过向服务器发送一个SYN包来发起与服务器的TCP连接。

  • 2.服务器回应:服务器接收到客户端的SYN包后,返回一个SYN-ACK包,表示同意建立连接。

  • 3.客户端确认:客户端接收到服务器的SYN-ACK包后,发送一个ACK包给服务器,表示连接已建立成功。

  • 4.发送HTTP请求:客户端与服务器建立了TCP连接后,客户端可以发送HTTP请求。HTTP请求包括HTTP方法(GET, POST等)、URI(Uniform Resource Identifier)和HTTP协议版本等信息。

  • 5.服务器响应:服务器接收到HTTP请求后,根据请求的内容进行处理,并返回HTTP响应。HTTP响应包括响应状态码(例如200表示成功,404表示未找到等)、响应头和响应体等信息。

  • 6.关闭连接:客户端接收到服务器的HTTP响应后,根据需要继续发送其他请求,或者关闭TCP连接。

这是一个简化的HTTP握手过程,实际上还有更多的细节和可选项,例如在建立TCP连接时可能需要进行三次握手以确保连接的可靠性,还有可能使用持久连接(Keep-Alive)机制来减少握手的次数等。HTTP握手过程中的这些步骤保证了客户端和服务器之间的通信顺利进行,并确保数据在网络传输时的可靠性和完整性。
抓包分析TLS握手过程

触发https请求

curl -k --insecure "https://www.baidu.com"
tcpdump -i any port 443 -nnnvvvv -w https.pcap

WireShark抓包验证SNI:

TLS握手过程是一个复杂的过程,包括以下步骤:

  • 1.客户端发送ClientHello报文:客户端发送一个ClientHello报文,该报文包含客户端支持的协议版本、会话ID、密码组和压缩方法。客户端提交的ClientHello所包含的密码组是客户支持的密码算法列表(按优先级降序排列),压缩方法是客户支持的压缩方法列表。
  • 2.服务器返回ServerHello报文:服务器发送一个ServerHello报文,该报文包含服务器建议的协议版本、服务器选择的密码组和压缩方法,以及两个随机数:ClientHello.random和ServerHello.random。
  • 3.服务器返回CA证书和请求客户端证书:服务器发送自己的证书(Certificate),Certificate 报文包含一个X.509证书或者一条证书链。除了匿名DH之外的密钥交换方法都需要发送Certificate报文。如果服务器需要被认证的话,服务器会发送自己的证书。同时,服务器可能会发送一个ServerKeyExchange报文,例如当服务器没有自己的证书或者证书仅用于签名时。
  • 4.客户端提交证书并发送ClientKeyExchange报文:客户端将自己的证书上报给服务器,同时会发送一个ClientKeyExchange报文,该报文包含密钥协商后的秘钥给服务器。
  • 5.客户端验证服务器的证书:客户端会验证服务器的证书的有效性。如果服务器的证书是有效的,客户端会发送一个ChangeCipherSpec报文和一个EncryptedHandshake消息。
  • 6.服务器发送ChangeCipherSpec报文和EncryptedHandshake消息:服务器也会发送ChangeCipherSpec报文和EncryptedHandshake消息,表示握手协商已经完成。
  • 7.应用层面交互:完成握手协商后,客户端和服务器会开始进行应用层面的交互,通过之前协商的秘钥来加密通信内容。

以上就是TLS握手过程的详细步骤。

Linux C/C++解析 Server Name Indication(SNI)字段

这是一个类似tcpdump的程序,用于在实时或捕获的流量中打印TLS SNI和HTTP/1.1主机字段。

char *tls_ContentType(uint8_t n)
{switch (n) {/* 20*/case SSL3_RT_CHANGE_CIPHER_SPEC:return "change_cipher_spec";/* 21 */case SSL3_RT_ALERT:return "alert";/* 22 */case SSL3_RT_HANDSHAKE:return "handshake";/* 23 */case SSL3_RT_APPLICATION_DATA:return "application_data";default:return "UNKNOWN";}return "";
}char *tls_AlertLevel(uint8_t n)
{switch(n) {/* 1 */case SSL3_AL_WARNING:return "warning";/* 2 */case SSL3_AL_FATAL:return "fatal";default:return "UNKNOWN";}return "";
}char *tls_AlertDescription(uint8_t n)
{switch(n) {/* 0 */case SSL3_AD_CLOSE_NOTIFY:return "close_notify";/* 10 */case SSL3_AD_UNEXPECTED_MESSAGE:return "unexpected_message";/* 20 */case SSL3_AD_BAD_RECORD_MAC:return "bad_record_mac";case 21:return "decryption_failed_RESERVED";case 22:return "record_overflow";case 30:return "decompression_failure";case 40:return "handshake_failure";case 41:return "no_certificate_RESERVED";case 42:return "bad_certificate";case 43:return "unsupported_certificate";case 44:return "certificate_revoked";case 45:return "certificate_expired";case 46:return "certificate_unknown";case 47:return "illegal_parameter";case 48:return "unknown_ca";case 49:return "access_denied";case 50:return "decode_error";case 51:return "decrypt_error";default:return "UNKNOWN";}return "";
}char *tls_HandshakeType(uint8_t n)
{switch(n) {/* 0 */case SSL3_MT_HELLO_REQUEST:return "hello_request";/* 1 */case SSL3_MT_CLIENT_HELLO:return "client_hello";/* 2 */case SSL3_MT_SERVER_HELLO:return "server_hello";/* 11 */case SSL3_MT_CERTIFICATE:return "certificate";/* 12 */case SSL3_MT_SERVER_KEY_EXCHANGE:return "server_key_exchange";/* 13 */case SSL3_MT_CERTIFICATE_REQUEST:return "certificate_request";/* 14 */case SSL3_MT_SERVER_DONE:return "server_hello_done";/* 15 */case SSL3_MT_CERTIFICATE_VERIFY:return "certificate_verify";/* 16 */case SSL3_MT_CLIENT_KEY_EXCHANGE:return "client_key_exchange";/* 20 */case SSL3_MT_FINISHED:return "finished";default:return "UNKNOWN";}return "";
}
...
int tls_set_callback_handshake_clienthello_servername(int (*handler)(uint8_t *, uint16_t));
int tls_process_Handshake_ClientHello_Extensions_ServerName();
int tls_process_Handshake_ClientHello_Extensions();
int tls_process_Handshake_ClientHello();
uint32_t tls_process_record(uint8_t *payload, uint32_t payload_length);
int http_set_callback_request_host(int (*handler)(uint8_t *host_name, uint16_t host_name_length));
int http_init();
uint16_t http_process_request(uint8_t *payload, uint16_t payload_length);
void http_cleanup();
int sni_handler (uint8_t *host_name, uint16_t host_name_length);int main (int argc, char *argv[])
{
...while ((i = getopt(argc, argv, "hf:pi:r:w:")) != -1) {switch(i) {case 'h':fprintf(stderr,"Use: %s [-h] [-f bpf] [-p] -i interface [-w dump.pcap]\n", argv[0]);fprintf(stderr,"Use: %s [-h] [-f bpf] [-p] -r trce.pcap [-w dump.pcap]\n", argv[0]);return -1;break;case 'f':bpf_s = optarg;opt_flags |= OPT_BPF;break;case 'p':opt_flags |= OPT_PROMISCUOUS;break;case 'i':device_name = optarg;opt_flags |= OPT_DEVICE;break;case 'r':trace_fname = optarg;opt_flags |= OPT_TRACE;break;case 'w':dump_fname = optarg;opt_flags |= OPT_DUMP;break;default:break;}}...fprintf(stdout, "[*] PID: %u\n", getpid());if (opt_flags & OPT_DEVICE) {fprintf(stdout, "[*] Device: '%s'\n", device_name);fprintf(stdout, "[*] Promiscuous: %d\n", PROMISCUOUS);if (!(pcap_handle =pcap_open_live(device_name, SNAPLEN, PROMISCUOUS, PCAP_TIMEOUT,errbuf))) {fprintf(stderr, "[FATAL] %s\n", errbuf);return -1;}}if (opt_flags & OPT_TRACE) {fprintf(stdout, "[*] Trace: '%s'\n", trace_fname);if (!(pcap_handle =pcap_open_offline(trace_fname, errbuf))) {fprintf(stderr, "[FATAL] %s\n", errbuf);return -1;}}...if (!(opt_flags & OPT_BPF)) {bpf_s = bpf_default;opt_flags |= OPT_BPF;}fprintf(stdout, "[*] BPF: '%s'\n", bpf_s);if (pcap_compile(pcap_handle, &bpf, BPF, BPF_OPTIMIZE,PCAP_NETMASK_UNKNOWN) == -1) {fprintf(stderr, "[FATAL] Couldn't parse filter. %s\n",pcap_geterr(pcap_handle));pcap_close(pcap_handle);return -1;}if (pcap_setfilter(pcap_handle, &bpf) == -1) {fprintf(stderr, "[FATAL] Couldn't install filter. %s\n",pcap_geterr(pcap_handle));pcap_close(pcap_handle);return -1;}pcap_freecode(&bpf);pcap_dumper_handle = NULL;if (opt_flags & OPT_DUMP) {fprintf(stdout, "[*] Dump: '%s'\n", dump_fname);if (!(pcap_dumper_handle = pcap_dump_open(pcap_handle, dump_fname))) {fprintf(stderr, "[WARNING] Couldn't create dump file. %s\n",pcap_geterr(pcap_handle));}}
...tls_set_callback_handshake_clienthello_servername(&sni_handler);http_set_callback_request_host(&sni_handler);http_init();act.sa_handler = signal_handler;sigemptyset (&act.sa_mask);act.sa_flags = 0;if (sigaction(SIGINT, &act, NULL)) {perror("sigaction");fprintf(stderr,"[WARNING] Failed to set signal handler for SIGINT.\n");}if (sigaction(SIGTERM, &act, NULL)) {perror("sigaction");fprintf(stderr,"[WARNING] Failed to set signal handler for SIGTERM.\n");}if (sigaction(SIGSEGV, &act, NULL)) {perror("sigaction");fprintf(stderr,"[WARNING] Failed to set signal handler for SIGSEGV.\n");}fprintf(stderr, "Capturing ...\n");if (pcap_loop(pcap_handle, -1, &my_pcap_handler, NULL) == -1) {fprintf(stderr, "[FATAL] pcap_loop failed. %s\n",pcap_geterr(pcap_handle));}if (!(opt_flags & OPT_TRACE)) {if (pcap_stats(pcap_handle, &ps) == -1) {fprintf(stderr, "pcap_stats failed. %s\n", pcap_geterr(pcap_handle));} else {fprintf(stderr, "%u packets received\n", ps.ps_recv);fprintf(stderr, "%u packets dropped\n", ps.ps_drop + ps.ps_ifdrop);}}pcap_close(pcap_handle);http_cleanup();if (pcap_dumper_handle) {pcap_dump_close(pcap_dumper_handle);fprintf(stderr, "Written %s\n", dump_fname);if (!(opt_flags & OPT_DUMP)) {free(dump_fname);}}...
}

If you need the complete source code, please add the WeChat number (c17865354792)

curl触发SNI字段解析

curl -k --insecure "https://www.baidu.com"
curl www.baidu.com

该程序从TLS握手客户端Hello消息中提取服务器名称指示(SNI)字段(RFC 4366),并从HTTP/1.1请求中提取主机请求头字段(RFC 2616)。

它接受一个网络接口作为输入来监控其流量(可选地以混杂模式),或者接受一个PCAP文件作为输入来读取。默认情况下,它使用BPF以目标端口为80或443的TCP数据包为目标,但如果相应配置,它将处理其他端口上的TLS和HTTP数据包以及UDP数据包。捕获的流量可以保存到PCAP文件中。

总结

SNI,即服务器名称指示,是TLS协议的扩展。它允许在握手过程开始时通过客户端告诉服务器正在连接的主机名称,从而解决一个服务器拥有多个域名的情况。

在TLS握手信息中并没有携带客户端要访问的目标地址,导致当一台服务器有多个虚拟主机,且每个主机的域名不一样,使用了不一样的证书时,不知道和哪台虚拟主机进行通信。而SNI允许Web服务器通过SSL或TLS握手的扩展在单个IP地址上托管多个站点,从而使得HTTPS网站具有唯一的TLS证书,即使它们位于共享IP地址上。使用SNI时,服务器的主机名包含在TLS握手中,这使得HTTPS网站具有唯一的TLS证书,即使它们位于共享IP地址上也是如此。

Welcome to follow WeChat official account【程序猿编码

参考:RFC 4366、RFC 2616

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/117196.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

旁注、目录越权、跨库查询、cdn绕过

原理: 搭建网站多IP多端口,更多一个域名多网站,IIS的在属性-高级里面设置主机头设置域名,域名是收费的需要自己买一个 旁注:在同一服务器上有多个站点,要攻击的这个站点假设没有漏洞,可以攻击…

设计模式中继承和组合的总结

一、什么是继承 继承就是子类继承父类的特征和行为,使得子类具有和父类一样的属性和方法,在Java中通过extends关键字来实现继承。 优点 继承最大的好处是可以代码复用。针对多个类有相同的属性和方法,可以将这些相同代码抽取出来形成统一父…

Kingbase备份与还原及表的约束(Kylin)

备份与还原 逻辑备份是对整个数据库好数据库中的部分对象利用逻辑备份工具导出数据到备份文件在需要数据恢复的情况下利用逻辑还原工具把备份文件恢复到数据库中 使用场景 逻辑备份主要用于数据库逻辑错误的恢复,恢复后对其他数据没有太大影响逻辑备份可用于在大…

vue重修之路由【下】

文章目录 版权声明路由重定向、404,路由模式重定向404路由模式 声明式导航vue-routerrouter-link-active 和 router-link-exact-active定制router-link-active 和 router-link-exact-active跳转传参两种跳转传参总结 编程式导航两种语法路由传参path路径跳转传参nam…

python3 修行之基础篇(一)python 简介

文章目录 python的发展史Python的特点查看 Python 版本简单Python3.x程序应用 python的发展史 Python 的发展历史可以追溯到上世纪80年代末期。以下是Python的一些重要的语言简介。 1989年: Guido van Rossum 在圣诞节期间开始设计 Python。1991年: Gu…

c++ 并发与多线程(12)线程安全的单例模式-2

一、内部静态变量的懒汉单例(C++11 线程安全) #include <iostream> #include <thread> #include <vector>class Single { public:// 获取单实例对象static Single& GetInstance();// 打印实例地址void Print();private:// 私有构造函数,防止外部创建对…

RHCE---搭建博客网站

一.实验要求&#xff1a; Server-NFS-DNS主机配置NFS服务器&#xff0c;将博客网站资源文件共享给Server-web主机&#xff0c;Server-NFS-DNS主机配置DNS Server-web主机配置web服务&#xff0c;通过域名www.openlab.com可以访问到自建的博客网站 二.准备工作 创建两台虚拟机…

python之客户端和服务端的例子

python之客户端和服务端的例子 1&#xff09;TCP服务器端 以下是一个简单的Python TCP服务器端的示例代码&#xff1a; import socket # 创建TCP套接字并绑定到本地地址和端口 server_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((lo…

C++11:智能指针(二)实战篇(附上实例)

目录 1、unique_ptr&#xff1a; 2、shared_ptr&#xff1a; 3、weak_ptr&#xff1a; 4、如何处理循环引用问题&#xff1f; C智能指针是一种用于管理动态分配的对象的指针&#xff0c;它可以自动释放所指向的对象&#xff0c;避免内存泄漏和悬空指针的问题。 C标准库提供…

Redis的优势

高性能 Redis是一种基于内存的数据存储系统&#xff0c;读写性能非常高&#xff0c;因此适用于对性能要求较高的应用场景。 数据结构丰富 Redis支持多种数据结构&#xff0c;如字符串、列表、集合、散列、有序集合等&#xff0c;可以满足不同的业务需求。还有一些特殊数据结…

Linux流量监控

yum install -y iptrafiptraf-ng -d ens33

关于近期IP-Guard新版本客户端重复发送邮件的问题处理说明

关于近期新版本客户端重复发送邮件的问题处理说明 一、问题描述 近期部分客户反馈,升级到新版本的客户端(4.81.341.0、4.82.621.0及以上),使用SMTP协议发送邮件时,会出现重复发送邮件的情况,主要表现为以下两种现象: Outlook发送包含大量收件人的邮件时,收件人邮箱可能…

11 结构型模式- 代理模式

结构性模式一共包括七种&#xff1a; 代理模式、桥接模式、装饰者模式、适配器模式、门面(外观)模式、组合模式、和享元模式。 1 代理模式介绍 软件开发中的代理&#xff1a; 代理模式中引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到了中介的作用,它去掉客…

LSTM 与 GRU

RNN无法处理长距离依赖问题&#xff0c;通俗点就是不能处理一些较长的序列数据&#xff0c;那么今天就来介绍一下两个能处理长距离依赖问题地RNN变种结构&#xff0c;LSTM和GRU。 1. LSTM&#xff08;Long short-term memory&#xff09; 1.1 LSTM结构 上左图是普通RNN结构图…

Windows下Eclipse C/C++开发环境配置教程

1.下载安装Eclipse 官网下载eclipse-installer&#xff08;eclipse下载器&#xff09;&#xff0c;或者官方下载对应版本zip。 本文示例&#xff1a; Eclipse IDE for C/C Developers Eclipse Packages | The Eclipse Foundation - home to a global community, the Eclipse ID…

#Day Day Plan# 《NCB_PCI_Express_Base 5.0.1.0》pdf 译文笔记

目录 前言碎语 第4章 Physical Layer Logic Block S4.1 Introduction S4.2 Logical sub-block S4.2.4 Link Initialization and Traning S4.2.4.3 Electrical Idle Sequence(EIOS) 译文&#xff1a;EIOS 电器空闲Ordered Set S4.2.7 Link Training and Status State R…

C语言-面试题实现有序序列合并

要求&#xff1a; a.输入两个升序排列的序列&#xff0c;将两个序列合并为一个有序序列并输出。 数据范围&#xff1a; 1≤n,m≤1000 1≤n,m≤1000 &#xff0c; 序列中的值满足 0≤val≤30000 输入描述&#xff1a; 1.输入包含三行&#xff0c; 2.第一行包含两个正整数n, m&am…

【强连通+背包】CF1763E

Problem - E - Codeforces 题意 思路 首先&#xff0c;先考虑第一个条件&#xff0c;要保证是p个节点互相到达且节点数最少&#xff0c;一定是个强连通&#xff0c;图的形态一定就是和强连通相关的。 然后&#xff0c;因为在这个前提上&#xff0c;要让单向节点数尽可能多&a…

【密评】商用密码应用安全性评估从业人员考核题库(十一)

商用密码应用安全性评估从业人员考核题库&#xff08;十一&#xff09; 国密局给的参考题库5000道只是基础题&#xff0c;后续更新完5000还会继续更其他高质量题库&#xff0c;持续学习&#xff0c;共同进步。 2501 多项选择题 GM/T 0047《安全电子签章密码检测规范》规定的电…

shell 中单引号,双引号,反引号的区别

1 单引号 str1hello echo $str1 打印结果是&#xff1a;$str1 总结&#xff1a; 单引号里的任何字符都会原样输出&#xff0c;单引号字符串中的变量是无效的&#xff0c;所以不能使用单引号 来解析变量。不过单引号可以引用变量。 例如&#xff1a; str1hello echo $str…