SSL/TLS 加密流量分析

加密流量分析,说白了就是要在不破解加密内容的前提下,把网络里的 TLS 流量看个大概。嗯,这活儿挺考验耐心。

我个人习惯把 TLS 分析分成四步:握手过程、证书链验证、SNI 字段提取、加密流分类。每一步都有坑,我一个个讲。

TLS 握手过程——不仅仅是“你好我好”

TLS 握手,本质上是在建立加密隧道之前,双方先互相打个照面,确认身份、商定密钥。我用 Wireshark 抓过上千个握手包,总结下来核心就这五步:

  1. Client Hello:客户端打招呼,告诉服务器它支持哪些 TLS 版本、密码套件,还带一个随机数。
  2. Server Hello:服务器从客户端列表里挑一个,选好版本和密码套件,也发个随机数。同时把自己的证书丢过来。
  3. 证书链与 Server Hello Done:服务器发完证书就说“我说完了”。
  4. 客户端密钥交换 + Change Cipher Spec:客户端生成随机数,用服务器公钥加密发过去。然后双方各自算出对称密钥。
  5. Handshake Finished:两边互相确认,握手结束。

你想想看,这里面最容易出问题的就是第三步。证书链如果断了一环,客户端就会报警。

避坑提示: 我在做 DLP 网关抓包分析时,发现很多内部系统用自签名证书,导致握手失败。记得提前把自签名证书加到信任库。

证书链验证——信任的链条不能断

服务器发的证书不是孤立的。它是一串链:服务器证书 → 中间 CA 证书 → 根证书。浏览器或 DLP 设备收到后,会从根开始逐级验证签名。

我曾在项目里遇到过一件事:某网银系统的中间 CA 证书在本地信任库里缺失了,结果所有 TLS 流量都被判定为“证书无效”。当时排查了整整一下午,最后发现是运维没上传完整证书链。真是坑。

证书层级 作用 常见问题
根证书 信任锚点,预置在操作系统或浏览器中 过期、撤销(CRL/OCSP)
中间CA证书 由根签发,负责签署服务器证书 链不完整、未上传
服务器证书 最终用于加密通信 域名不匹配、有效期短

做 DLP 扫描时,建议把证书链验证结果作为一个特征维度。比如“链长度”、“根证书是否在可信列表”。这些都能帮你区分合法流量和恶意流量。

SNI 字段提取——加密流量的“门牌号”

TLS 加密后,你没法直接看到 HTTP Host 头。但 Client Hello 里有个明文字段叫 SNI(Server Name Indication)。它告诉服务器你要访问哪个域名。这个字段是明文,没加密。

我记得第一次写 SNI 提取脚本时,踩了个坑:有些客户端(比如旧版 IE)不支持 SNI,Client Hello 里就没这个字段。另外,如果流量走的是代理隧道,SNI 可能会被覆盖。所以提取时一定要做空值判断。

# 用 scapy 提取 SNI 的简单示例(Python)
from scapy.all import *

def extract_sni(packet):
    if packet.haslayer(TLS):
        tls_layer = packet[TLS]
        for msg in tls_layer.msg:
            if msg.content_type == 22:  # Handshake
                handshake = msg
                for ext in handshake.extensions:
                    if ext.type == 0:  # server_name
                        sni = ext.server_name
                        return sni.decode('utf-8', errors='ignore')
    return None
注意: SNI 只能帮你看到域名,看不到路径和参数。如果客户端用了 ECH(加密 Client Hello),SNI 也会被加密。不过现阶段 ECH 部署还很少。

加密流分类——给流量打标签

有了握手信息和 SNI,就能对 TLS 流进行分类了。我一般从三个维度下手:

  • 基于 SNI 域名:白名单/黑名单匹配。比如 *.bank.com 归为金融类。
  • 基于 JA3 指纹:Client Hello 中的密码套件、扩展、椭圆曲线等组合成一个哈希值。不同客户端/工具的特征不同。例如,curl 和 Chrome 的 JA3 不一样。
  • 基于证书特征:颁发者、有效期、公钥长度等。

举个实际例子。我在某个项目里,靠 JA3 识别出了内网一台机器在用 Python requests 库大量访问外部 API。运维原以为是正常的浏览器流量,一查 JA3 指纹才发现是自动化脚本。

分类维度 提取方式 优势 不足
SNI Client Hello 明文 直接、低开销 依赖客户端支持,可能缺失
JA3 握手参数哈希 识别工具/客户端版本 需要维护 JA3 特征库
证书 服务器证书解析 判断可信度、到期时间 解析开销稍大
我的建议: 三种方式组合使用,效果最好。比如先看 SNI 是否在白名单,不在的话再对比 JA3 黑名单。如果发现证书自签发且 JA3 类似恶意工具,直接阻断。
TLS 加密流量分析核心流程 Client Hello(含SNI) Server Hello + 证书链 证书链完整性验证 SNI 字段提取 JA3 指纹哈希 加密流分类(SNI+JA3+证书)

老实说,这套流程看似简单,但实际部署时麻烦不少。比如在高并发场景下,JA3 计算会消耗 CPU;或者 SNI 字段可能被恶意篡改(虽然少见)。

我曾经遇过一个案例:某台被控的服务器用自定义 TLS 客户端,它的 JA3 指纹根本不在公开库里。后来我抓了它的证书特征——自签名、有效期 10 年、公钥是 512 位——立刻判定为可疑流量。最终确认是挖矿木马。

经验总结: 别只依赖一个特征。SNI 可能被抹掉,JA3 可能被伪造,证书可能被替换。三个维度互相印证,准确率才能上 95%。