ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

SSL/TLS握手过程与密码套件协商:从原理到CVE-2016-2183漏洞排查

SSL/TLS握手过程与密码套件协商:从原理到CVE-2016-2183漏洞排查 SSL/TLS 的握手过程是我这两年被问得最多的高频八股之一几乎每次技术面试都会被拿出来当“试金石”。面试官爱问这个问题不奇怪这一过程能把证书验证、非对称加密、对称加密、哈希校验、随机数生成这些知识点全部串起来答得好说明基础扎实。但最近群里有朋友发来一张告警截图内容是 Windows 3389 端口上监测到 SSL/TLS 协议信息泄露漏洞CVE-2016-2183问怎么排查和修复。这一下把我拉回现实很多人“会背握手过程”但真的遇到密码套件协商这种落地问题时还是会懵。这篇文章我打算换个讲法不光是背八股而是把握手过程拆到抓包数据包层面再结合 CVE-2016-2183 这个真实漏洞案例讲清楚握手协商机制到底哪里有坑以及排查手段。1. 先搞懂握手过程在整个 TLS 体系里是什么角色1.1 从一次 HTTPS 访问说起你在浏览器里输入https://example.com按下回车浏览器底部状态栏通常会先转几圈然后出现一把锁。这几圈里浏览器和服务器之间发生的事就是 TLS 握手。TLS 的全称是 Transport Layer Security前身是 SSLSecure Sockets Layer因为历史原因大家习惯叫 SSL/TLS。它的作用概括起来就三件事加密通信内容、验证对方身份、保证数据完整性。但有个细节很多人没想过如果直接用非对称加密比如 RSA加密所有业务数据性能会非常差。非对称加密的计算开销比对称加密高几个数量级一个 2048 位 RSA 私钥操作可能要花 AES 密钥加密同样大小数据几百上千倍的时间。所以 TLS 的工程实现采用“混合加密”思路用非对称加密安全地协商出一个临时会话密钥后续业务数据全部用对称加密比如 AES来加解密。这个“协商临时密钥”的过程就是握手过程本身。1.2 握手在 TLS 协议栈里的位置一个完整的 TLS 协议栈从下到上大致是TCP 层、TLS 记录协议Record Protocol、TLS 握手协议Handshake Protocol再往上是 HTTP、FTP 等应用层数据。记录协议负责把数据分块、压缩可选、加加密 MAC、再通过 TCP 发送握手协议则负责在正式传输数据之前完成版本协商、密码套件协商、身份认证和密钥交换。换句话说记录协议是“运输工具”握手协议是“安全交接流程”。抓包时你在 Wireshark 里看到的一堆 ClientHello、ServerHello、Certificate、Finished就是握手协议跑出来的报文。理解这一点后面抓包看记录层格式就会容易很多否则看到一堆十六进制字段基本是看了个寂寞。1.3 为什么面试官总爱把握手过程当“必考题”从面试角度看握手过程能考察几个层次第一层是“背流程”说明你见过这个知识点第二层是“讲原理”说明你理解证书、密钥交换、随机数的作用第三层是“结合实际”比如问 CVE-2016-2183 是什么或者问为什么有的服务器强制禁用 3DES这要求你把握手中的“密码套件协商”环节和真实安全事件联系起来。我自己面试别人的时候通常会从“握手需要几个往返”问起然后追问“你抓过包吗”。大部分候选人能答出 RSA 密钥交换和 1.2 的流程但问到“ServerKeyExchange 什么场景会出现”“为什么 1.3 快那么多”就卡壳了。这说明八股背熟了但协议栈没有真正“跑”过。所以这篇文章后面会用 Wireshark 实际抓一个 TLS 握手包逐条对照解释顺便把 CVE-2016-2183 这种漏洞的排查思路串进去让大家从“会背”升级到“会聊”。2. TLS 1.2 完整握手流程拆解含密钥协商原理2.1 第一阶段ClientHello 到底在“聊”什么TLS 握手的第一步是客户端向服务器发送一个 ClientHello 消息。这一步的目的不是传业务数据而是“打招呼 报菜单”。里面携带的关键字段包括客户端支持的 TLS 版本列表比如 TLS 1.0、1.1、1.2、1.3客户端支持的密码套件列表Cipher Suites这是一份长长的“菜单”可能包含几十个套件一个客户端生成的随机数 ClientHello.random32 字节后面生成主密钥要用可选的 Session ID 或 Session Ticket用于会话恢复SNIServer Name Indication扩展用于在同一个 IP 上放多个证书的场景。抓包时你会在 Wireshark 里看到类似TLSv1.2 Record Layer: Handshake Protocol: Client Hello这样的结构。点开后能看到一个很长的 Cipher Suites 列表像TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256这种。这里有个关键点客户端把菜单端上来但最终点哪个菜由服务器决定。这就是密码套件协商的开始也是 CVE-2016-2183 这类漏洞出现的第一个入口因为如果“菜单”里有弱套件而服务器允许选择它后续安全强度就被拉低了。2.2 第二阶段ServerHello、证书和 ServerKeyExchange服务器收到 ClientHello 后会回一个 ServerHello内容包括选定的 TLS 版本比如 TLS 1.2从客户端菜单里挑中的密码套件服务器随机数 ServerHello.random如果是会话恢复还会带上 Session ID。紧接着服务器一般会把证书发送给客户端也就是 Certificate 消息。证书里面包含服务器公钥、证书签发机构CA的信息、有效期、签名等。客户端收到后会做一件很重要的事验证证书链是不是可信的。如果证书是自己签的、过期了、或者域名不匹配浏览器就会拦下连接并报错。这个过程在最开始面试的时候经常被忽略但它恰恰是防止中间人攻击的关键一步。在某些场景下服务器还会发一条 ServerKeyExchange 消息。比如选用 DHEEphemeral Diffie-Hellman或 ECDHE 密钥交换算法时服务器需要临时生成一组 DH 参数发过去。如果是 RSA 密钥交换这条消息一般可以省略因为服务器的 RSA 公钥已经在证书里了。这就是一个很实在的八股点看到 ServerKeyExchange 出现你就要意识到密钥交换用的是 DHE/ECDHE而不是 RSA。最后服务器发送 ServerHelloDone表示“我这边能说的都说完了轮到你了”。2.3 第三阶段客户端密钥交换与 Finished 校验客户端收到 ServerHelloDone 后首先要验证服务器证书。验证内容包括证书链、签名、有效期、主机名有的场景还会检查证书吊销状态OCSP。一项验证没过握手直接终止。验证通过后客户端要生成“预主密钥”pre-master secret。如果使用 RSA 密钥交换客户端生成一个 48 字节的随机数其中前两个字节是协议版本号剩下 46 字节随机数然后用服务器公钥加密通过 ClientKeyExchange 消息发给服务器。如果是 ECDHE 密钥交换客户端会生成自己的临时 DH 私钥/公钥对把公钥参数发过去。到这里双方手里都有了 client_random、server_random、pre-master secret然后把这三者按约定方式做 PRF伪随机函数运算生成最终的主密钥master secret再从主密钥派生出后续用于对称加密的密钥、MAC 密钥和 IV。这些细节你不用背公式但记住一个核心pre-master secret 只有客户端和服务器知道任何第三方都拿不到。随后客户端发送 ChangeCipherSpec意思是“我开始切换到对称加密通信了”。紧接着发送 Finished 消息里面是一段用会话密钥加密的握手消息摘要。服务器收到后用相同方式解密并校验校验通过则说明之前所有消息都没有被篡改。服务器同样回一个 ChangeCipherSpec Finished客户端做同样的校验。到这里握手完成进入应用数据传输阶段也就是真正收发 HTTP 请求响应。2.4 会话恢复1-RTT 的秘密完整握手需要两个往返2-RTT这在高延迟网络下体验不好。所以 TLS 设计了会话恢复机制。简单说服务器用 Session ID 把会话状态缓存起来客户端再次连接时ClientHello 里带上之前的 Session ID如果服务器还记得直接跳过证书重传和密钥交换步骤双方重新算一遍密钥握手变成 1-RTT。更现代的做法是 Session Ticket。服务器把会话状态加密成 ticket 发给客户端客户端下次直接带上 ticket服务器解密验证通过后即可恢复会话不占用服务器内存。这个机制在你的抓包里非常常见同一台机器第一次连是完整握手马上再连第二次可能就是 Abbreviated Handshake这就是 1-RTT 的体现。很多面试题会问“TLS 1.3 为什么能做到 1-RTT甚至 0-RTT”本质就是从这些扩展机制演化过来的。3. 实战抓包分析 SSL/TLS 握手过程3.1 抓包前的环境准备要真正把握手过程看出门道建议自己动手抓一次。我常用的组合是 Wireshark curl 或浏览器访问 HTTPS 站点。如果没有图形界面用 tcpdump 抓 pcap 文件再放到 Wireshark 里分析也是一样。一个最小化操作流程是打开 Wireshark选择你的网卡如果是本机访问本地服务选 loopback 接口设置抓包过滤器为tcp port 443或者更精确的host example.com and tcp port 443启动抓包后用浏览器或者curl -v https://example.com发起一次 HTTPS 请求停止抓包然后在显示过滤器里输入tls或者ssl找到第一条 ClientHello右键选择 Follow - TLS Stream能看到整个握手报文序列。这个抓包过程我建议所有人至少完整做一次。我面试过的不少候选人能把握手流程背得一字不差但问他“ClientHello 的 record 层和 handshake 层分别在哪几层”就含糊了。实际抓包后你会发现一个 ClientHello 报文会被记录层先包一层记录层头部包含版本、类型、长度然后再装握手层数据。这种层级关系光靠读文档很难形成肌肉记忆。提示抓自己本机的包尽量用 HTTPS 访问一个你完全信任的站点。不要尝试拦截别人网络中的流量那涉及安全和法律风险。3.2 一个标准 TLS 1.2 握手的报文序列抓包完成后你通常会在 Wireshark 里看到这样的顺序序号源 - 目的协议信息概要1客户端 - 服务器TLSv1.2Client Hello2服务器 - 客户端TLSv1.2Server Hello3服务器 - 客户端TLSv1.2Certificate4服务器 - 客户端TLSv1.2Server Key Exchange5服务器 - 客户端TLSv1.2Server Hello Done6客户端 - 服务器TLSv1.2Client Key Exchange7客户端 - 服务器TLSv1.2Change Cipher Spec8客户端 - 服务器TLSv1.2Encrypted Handshake Message9服务器 - 客户端TLSv1.2Change Cipher Spec10服务器 - 客户端TLSv1.2Encrypted Handshake Message打开 Server Hello 那一条展开 Handshake Protocol - Cipher Suite 字段你能看到服务器最终选中的密码套件。比如TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256这条套件包含几层含义密钥交换用 ECDHE证书认证用 RSA对称加密用 AES-128-GCM完整性校验用 SHA256。如果这里选中的是TLS_RSA_WITH_3DES_EDE_CBC_SHA那就要引起警惕这正好就是 CVE-2016-2183 影响的弱套件之一。再展开 Certificate 消息能看到服务端证书双击证书字段Wireshark 甚至可以把证书导出然后你可以在系统里查看证书链。实操中我经常用这个方式快速确认服务器下发的证书是不是预期的那个尤其是排查证书过期或者多证书配错的问题。有一次我帮朋友排查内网 HTTPS 服务异常发现服务端下发的是自签名证书而客户端要求验证 CA最后定位到配置里挂错了证书文件抓包只需要几十秒就锁定了。3.3 密钥计算与 TLS 1.3 的变化在 TLS 1.2 里客户端发送 ClientKeyExchange 之后双方各自用 client_random server_random pre-master secret 算出 master secret。这个计算过程用 PRF 完成PRF 的输入在 1.2 里跟协议版本强相关所以 TLS 1.0/1.1/1.2 的密钥派生方式不完全一样这也是为什么协议版本协商很重要如果双方最终协商出一个老版本后续整个安全强度都会变差。Wireshark 的 “Decrypt TLS” 功能可以在你拿到服务器私钥的前提下解密会话数据前提是你在抓包前配置好 RSA keys 或 (Pre)-Master-Secret 日志。Chrome 和 Firefox 可以通过设置SSLKEYLOGFILE环境变量导出密钥日志这个技巧在分析混合加密细节时非常好用但要注意不要把私钥和密钥日志文件公开出去。TLS 1.3 的握手结构变化非常大。它把 ServerHello 之后的内容做了合并很多消息被精简整个握手只需要 1 个往返。密码套件列表也大幅缩短移除了 RSA 密钥交换和 CBC 模式不支持 3DES、RC4 这些弱算法。这就是为什么在很多安全基线里推荐把最低 TLS 版本设置为 1.2 并开放 1.3而不要后退到 1.0/1.1那个版本区间的薄弱点特别多。3.4 抓包排查中最常见的三种“握手失败”抓包不只是为了看流程更是为了排查问题。我归纳了工作中最常见的三种握手失败场景客户端报“证书不受信任”。看抓包Certificate 消息已经发出但 Application Data 阶段没有任何数据客户端直接发了一个 Alert 消息通常是Handshake Failure或者Certificate Unknown。对策是检查证书链、证书有效期、颁发者以及客户端是否有对应根证书。握手反复重传迟迟没有 ServerHello。这种情况通常是服务器端防火墙或负载均衡把 TLS 流量拦截了或者后端服务只监听了一个 IP 的某个端口。抓包看到大量 TCP 重传ClientHello 发出去没有响应就要往网络层排查。版本协商失败客户端和服务端支持的最高版本不一致。比如客户端只支持 TLS 1.3服务器只支持 TLS 1.0握手里会在 ServerHello 直接返回 AlertProtocol Version。这种问题在老旧系统和现代浏览器之间特别常见也是很多合规扫描报告里出现“SSL/TLS 协议信息泄露漏洞”的触发场景之一。4. CVE-2016-2183 和 Windows 3389 端口握手协商里的真实漏洞4.1 SWEET32 漏洞是什么一回事CVE-2016-2183 是 2016 年公开的漏洞属于 SWEET32 攻击技术的一部分影响的算法核心是 3DES。你可能在安全扫描报告里经常看到类似“SSL/TLS 协议信息泄露漏洞(CVE-2016-2183)”、“Windows 3389 端口 SSL/TLS 协议信息泄露漏洞”这样的描述它指向的就是 3DES 之类的 64 位分组密码套件。为什么 64 位分组有风险因为分组加密算法如果分组长度太短在 CBC 模式下长时间传输大量数据后会以一定概率出现碰撞。攻击者通过被动监听大量密文分组等到两个密文分组相同就可以利用碰撞推断出部分明文信息。3DES 的分组长度是 64 位根据生日攻击原理大约在 2^32 个分组约 32GB 数据之后就有较大概率出现碰撞。随着当今网络带宽不断提升这个数据量不再是“天文数字”所以 3DES 在 TLS 握手里被视为高风险算法。做个简单对比算法分组长度碰撞需要的密文量状态3DES64 bit约 32GB不建议使用AES-128128 bit约 2^64 分组以上安全AES-256128 bit同上安全在握手过程的语境里这一步对应的是“密码套件协商”。客户端在 ClientHello 里送上支持的套件如果在客户端和服务端的共同列表中包含 3DES且没有更优的 AES 套件被选择服务端就可能选中TLS_RSA_WITH_3DES_EDE_CBC_SHA这类套件。一旦这个套件被建立攻击者开始监听大量数据最终可能恢复出认证 Cookie、会话令牌等敏感信息。4.2 为什么 Windows 3389 端口常出现这个告警Windows 3389 端口是远程桌面服务RDP默认监听端口。RDP 在较新的 Windows 版本中使用 CredSSP 和 TLS 进行加密保护部分场景下会把 NLA网络级身份验证和 TLS 组合使用。如果 Windows 系统启用 TLS 时密码套件列表里仍然保留了 3DES 套件那么扫描器就会在 3389 端口上探测到 SSL/TLS 协议信息泄露漏洞CVE-2016-2183。很多企业内网的老旧 Windows Server 默认密码套件顺序可能包含 3DES尤其当系统未打补丁或组策略没有显式禁用旧套件时这个扫描告警很容易出现。实际等保和渗透测试报告中3389 端口报 CVE-2016-2183 的比例很高就是因为它默认开启了 TLS 但密码套件策略比较宽松。从这里也能看出理解握手过程不光是面试用遇到安全通告时你真的能知道问题出在哪个环节。这个漏洞发生在握手第一个阶段的“密码套件协商”完成后导致双方选择了强度不足的加密算法而不是证书机制或随机数生成器的问题。4.3 手工验证与抓包观察弱套件在修复之前最好先确认目标端口到底有没有暴露弱套件。我常用的方法是用 openssl 从外部做一次探测# 检查某个主机 3389 端口支持的 TLS 套件 openssl s_client -connect 192.168.1.10:3389 -tls1_2不过这条命令会进入一个交互式命令行你可以在里面输入Q退出。如果想快速枚举支持的套件更简单的是用sslscan或nmap --script ssl-enum-ciphersnmap -p 3389 --script ssl-enum-ciphers 192.168.1.10执行后你会看到目标端口支持的 TLS 版本列表和密码套件列表。如果看到类似TLS_RSA_WITH_3DES_EDE_CBC_SHA的套件标记为弱加密那基本可以判定漏洞存在。抓包层面你可以在本地起一个 RDP 会话用 Wireshark 过滤tcp.port 3389 and tls在 ServerHello 的 Cipher Suite 字段里直接看到协商结果这是最直观的证明。注意对没有授权的系统做端口扫描和套件探测在任何环境下都可能违反安全规定。请先确认你有合法授权或者在自己的测试机上实验。4.4 修复与加固从密码套件协商入手修复 CVE-2016-2183 的思路非常清晰让 3DES 不出现在协商菜单里或者阻止服务器选择它。具体措施如下升级系统补丁。微软在相关安全更新中改进了密码套件默认策略打过补丁后部分场景下 3DES 默认从系统优先级列表移除。禁用 3DES 密码套件。在 Windows Server 上可以通过组策略或注册表调整 Schannel 的密码套件顺序。比较稳妥的做法是启用AES 128/128和AES 256/256把Triple DES 168取消勾选并确保RC4相关项也禁用。配置 RDP 安全层。将远程桌面设置为“使用网络级别身份验证”并启用 TLS 1.2相当于在握手阶段主动抬高版本门槛。在 TLS 网关或负载均衡层面过滤弱套件。如果 3389 端口前面有网关或者这是一台 Web 服务器可以直接在 Nginx、IIS 或云负载均衡配置里显式禁止 3DES。Nginx 的示例配置如下ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5:!3DES; ssl_prefer_server_ciphers on;!3DES就是把 3DES 套件从名单里干掉。即使客户端菜单里有 3DES服务端也不会选中它。这个思路同样适用于其他服务TLS 握手的最终决策权在服务端手里所以服务端的套件白名单/黑名单策略非常重要。在 Windows 上一个常见的做法是通过 PowerShell 查询当前的 TLS 密码套件顺序Get-TlsCipherSuite | Format-Table Name然后可以用Enable-TlsCipherSuite和Disable-TlsCipherSuite精确控制。比如# 禁用 3DES 相关套件 Disable-TlsCipherSuite -Name TLS_RSA_WITH_3DES_EDE_CBC_SHA执行完重启相关服务再用 nmap 或 sslscan 复查如果弱套件不再出现告警就能消掉。这个排查恢复闭环本质上就是从握手协商的“Cipher Suite”字段入手因为它记录了双方到底选了什么加密算法安全扫描器的工作方式也和这个逻辑一致主动发起握手从 ServerHello 里提取服务端支持的套件然后跟弱算法库比对。5. 面试追问与实操经验补充5.1 握手过程相关高频追问清单除了“背流程”面试官还会从握手过程延伸出一堆问题提前准备能少踩不少坑。我列几个被问到的真实问题还有最容易踩的答法问题 1TLS 1.2 和 TLS 1.3 的握手差在哪1.3 把握手压缩到 1-RTT客户端在 ClientHello 里直接携带密钥共享参数服务端在 ServerHello 返回协商结果后双方马上就能算密钥。同时 1.3 废除了 RSA 密钥交换、CBC 模式、SHA-1、RC4、3DES只保留了前向保密算法例如 ECDHE。回答的时候最好顺带提一句前向保密能明显加分。问题 2Session Ticket 和 Session ID 恢复会话有什么区别Session ID 是服务端保存会话状态Session Ticket 是服务端把状态加密后发给客户端服务端不存状态。集群环境下 Ticket 更有优势因为请求可以落在不同节点但需要统一配置 Ticket 密钥否则节点之间无法解密彼此的 Ticket。问题 3什么是前向保密即使服务器的长期私钥泄露攻击者也无法解密历史上记录下来的加密流量。实现方式是使用 DHE/ECDHE 这类临时密钥交换算法每次会话的密钥都是临时生成的不和长期私钥绑定。这也是为什么现在安全基线普遍要求禁用静态 RSA 密钥交换。问题 4CVE-2016-2183 为什么会影响握手因为握手协商阶段选中的密码套件包含 3DES这是一个 64 位分组算法长时间大量加密数据后存在碰撞泄露风险。修复手段是禁用 3DES 套件让服务器不会选中它。问题 5客户端发送的密码套件列表很长服务端怎么选服务端不是挑第一个而是按照自己的套件优先级列表从上到下匹配客户端列表里的第一个交集项。所以服务端配置顺序极其重要这也是为什么安全基线要求服务端把强套件放在前面。5.2 从“会背”到“会讲”我的实操建议如果你想真正掌握握手过程光看文章不够建议动手做三件事第一抓包看一次完整握手。虽然上面给了推荐流程但我还是建议你把每个包点开逐字段看一遍。不要只看协议层次要点开 ServerHello 里的 Cipher Suite、Certificate 里的证书详情、Finished 的 Encrypted Handshake Message。这样你对“握手在数据包层面到底长什么样”会有具体认知。第二在本地起一个只支持 TLS 1.2 的测试服务分别用 Chrome、curl、openssl 连接然后观察不同客户端发送的 ClientHello 有哪些差异。你会发现有的客户端支持 TLS 1.3有的只支持到 1.2这对理解业务兼容性很有帮助。第三用 openssl 查看一个站点的证书链和套件openssl s_client -connect example.com:443 -servername example.com -showcerts-servername会带上 SNI 扩展才能拿到对应域名的证书。遇到证书配置错误、套件顺序有问题的时候这条命令是最快的诊断手段。5.3 我在实际工作中踩过的几个坑第一个坑是用 Wireshark 抓包时开了解密功能结果忘记关闭导致记录了很多敏感密钥日志。排查结束一定要清理SSLKEYLOGFILE这个文件一旦泄露别人能直接解密你的 TLS 流量。第二个坑是只关注了服务端套件配置忽略了客户端。比如把 Windows 3389 端口的 3DES 禁用了但客户端旧机器如果只支持 3DES连不上。所以修复前最好先确认客户端兼容范围再决定是彻底禁用还是通过优先级降级。第三个坑是修完不复查。安全扫描器报了个“CVE-2016-2183”修复后一定要重新扫描而且最好用抓包确认 ServerHello 里已经没有 3DES 套件。我遇到过申请了漏洞消除报告之后服务还在用弱套件的情况就是因为只改了 Nginx 配置但没重载握手结果没有变化。最后一个个人心得每次排查 SSL/TLS 问题我都会提醒自己先看握手的第一个环节密码套件协商。无论是加密强度不够、协议版本过低还是不兼容导致连接失败绝大多数问题都藏在 ClientHello 和 ServerHello 这两条消息里。把抓包工具用熟把协商过程看透很多安全告警都不再是黑盒而是一目了然的逻辑问题。
返回列表