VLESS-Reality 密钥套件与 SNI 伪装机制深度破译:借用境外大厂证书与抗 GFW 主动嗅探硬化指南

从 TLS 1.3 握手指纹、X25519 密钥协商到 SNI 借用与 dest 回落链路,逐层拆解 VLESS-Reality 的抗主动探测机制。包含 Mihomo/Sing-box 生产级配置、XTLS-Vision 流控参数、nftables 硬化规则与抓包审计清单,直击 GFW 主动嗅探与 TLS-in-TLS 检测痛点。

本文解决什么问题?
01 GFW 主动探测(Active Probing)对自签证书节点的识别:TLS 握手返回证书链与 SNI 不匹配,触发 SNI 黑名单与 IP 封禁
02 Reality 借用真实境外大厂证书的 X25519 密钥协商机制:客户端如何用服务端公钥验���临时证书签名,实现零证书部署
03 XTLS-Vision 流控与 splice 零拷贝在 TLS-in-TLS 场景下的性能落地,以及 dest 回落链路的正确配置
04 通过 tcpdump/Wireshark 抓包验证 ClientHello 指纹、JA3/JA4 哈希与 SNI 一致性,排查 DNS/IP 泄漏

01. 现实痛点与底层机理解构

先说结论:GFW 对代理协议的围剿早就过了”看流量特征”的阶段。2020 年之后,主动探测(Active Probing)成为主力手段——防火墙会主动向可疑 IP 的开放端口发起连接,根据响应行为判定是否为代理服务。这套机制对传统 TLS 伪装是降维打击。

1.1 传统 TLS 伪装的三个致命破绽

绝大多数”TLS 伪装”方案(Trojan、VMess+TLS、VLESS+TLS)的底层逻辑是:服务端持有一张证书,客户端校验证书后建立加密通道。问题出在证书本身。

破绽一:证书链不匹配。 你用 example.com 做 SNI,但服务端返回的是自签证书或者 Let’s Encrypt 签发的 example.com 证书。GFW 探测时执行一次标准 TLS 握手,对比 SNI 与证书 CN/SAN,如果证书是自签的、或者证书签发者不是主流 CA、或者证书链不完整,直接标记。更狠的是,GFW 会缓存这个 IP 的证书指纹,下次你换端口换域名,证书指纹一致照样封。

破绽二:握手行为异常。 真实网站的 TLS 握手有完整的 ServerHello、Certificate、CertificateVerify、Finished 流程,还会协商 ALPN(h2/http1.1)。如果你的服务端只返回 ServerHello 就进入数据阶段,或者 ALPN 协商结果为空,探测脚本一眼识破。

破绽三:TLS-in-TLS 指纹。 这是最隐蔽也最致命的。当你在 TLS 隧道里再跑一层 TLS(比如代理内部访问 HTTPS 网站),外层 TLS 记录的长度分布会呈现明显的规律性——因为内层 TLS 的握手包大小是固定的,外层加密后长度也相对固定。GFW 通过统计 TLS 记录长度的熵值就能识别。这个特征在 2022 年被大规模用于封禁 Trojan 节点。

1.2 Reality 的核心思路:不持有证书,借用别人的

Reality 的设计哲学非常激进:服务端根本不部署证书。它把 TLS 握手的证书部分”外包”给一个真实存在的境外大厂站点(称为 dest,比如 www.microsoft.com:443)。

具体流程是这样的:

  1. 客户端发起 ClientHello,SNI 填 www.microsoft.com,同时在 ClientHello 的 key_share 扩展里塞入一个 X25519 公钥(这是 Reality 的临时密钥)。
  2. 服务端收到 ClientHello,识别出这是 Reality 客户端(通过 session id 里的 shortId 和 key_share 的特殊构造),于是主动向 dest 发起一次真实的 TLS 握手,把 dest 返回的证书链原封不动转发给客户端。
  3. 服务端用自己持有的 X25519 私钥对临时证书签名,通过 CertificateVerify 消息发给客户端。
  4. 客户端用预置的 publicKey 验证这个签名,验证通过才继续握手。

关键在于:GFW 探测时拿到的证书,就是 www.microsoft.com 的真实证书。证书链完整、签发者是 DigiCert、SAN 包含 *.microsoft.com,与正常访问微软官网完全一致。GFW 无法通过证书判定这是代理。

那 GFW 能不能通过”这个 IP 不是微软的 IP 却返回微软证书”来判定?理论上可以,但实际操作中,CDN 场景下大量第三方 IP 都会返回大厂证书(比如 Cloudflare 边缘节点返回 *.cloudflare.com 证书),GFW 不可能把所有返回大厂证书的非官方 IP 全封了。这就是 Reality 的生存空间。

1.3 X25519 密钥协商与临时证书签名

Reality 的认证核心是 X25519 椭圆曲线密钥交换。服务端生成一对长期密钥:

# 使用 sing-box 生成 X25519 密钥对
sing-box generate reality-keypair
# 输出示例:
# PrivateKey: 8F3a...(服务端保存)
# PublicKey:  xY7b...(客户端配置)

客户端在 ClientHello 的 key_share 扩展里放入一个临时 X25519 公钥(记为 C_pub),服务端用自己的私钥和 C_pub 做 ECDH 计算出共享密钥 S,然后用 S 对临时证书签名。客户端用 publicKey 和 C_pub 对应的私钥算出同样的 S,验证签名。

这套机制的精妙之处在于:签名密钥是每次握手临时生成的,即使 GFW 抓到了某次握手的签名,也无法复用到下一次。而且整个认证过程完全藏在标准 TLS 1.3 握手里,不产生任何额外流量。

1.4 TLS 1.3 握手特征与 JA3/JA4 指纹

GFW 现在还会计算 ClientHello 的 JA3/JA4 指纹。JA3 是 2017 年 Salesforce 提出的 TLS 指纹算法,提取 ClientHello 的以下字段做 MD5:

  • TLS Version
  • Cipher Suites(密码套件列表)
  • Extensions(扩展列表)
  • Elliptic Curves(椭圆曲线列表)
  • Elliptic Curve Point Formats

JA4 是 2023 年的升级版,加入了 SNI 类型、ALPN、签名算法等维度。

Reality 客户端的 JA3 指纹取决于底层 TLS 库。如果你用 Go 的 crypto/tls,JA3 指纹是固定的(Go 的 ClientHello 构造很规范);如果用 uTLS 库,可以伪装成 Chrome 或 Firefox 的指纹。

# Mihomo 中配置 uTLS 指纹伪装
reality-opts:
  public-key: "xY7b..."
  short-id: "0123456789abcdef"
client-fingerprint: chrome  # 可选 chrome / firefox / safari / ios / android / edge / 360 / qq

这里有个大坑:client-fingerprint 必须和 dest 站点的真实指纹匹配。如果你伪装成 Chrome 指纹,但 dest 是 www.microsoft.com,而微软官网对 Chrome 和 Firefox 返回的证书链可能不同(SNI 分流),会导致握手失败。建议选 chrome,因为绝大多数大厂站点对 Chrome 支持最好。


02. 网络拓扑与核心交互设计

2.1 整体拓扑

┌─────────────┐         ┌──────────────────────────────────────┐         ┌─────────────┐
│   Client    │         │           GFW / 中间设备              │         │  Reality    │
│ (Mihomo/    │         │                                      │         │  Server     │
│  Sing-box)  │         │  ┌────────────────────────────────┐  │         │ (VPS)       │
│             │         │  │ 1. 被动流量分析                 │  │         │             │
│  ┌───────┐  │         │  │    - TLS 记录长度熵             │  │         │  ┌───────┐  │
│  │ App   │  │         │  │    - JA3/JA4 指纹               │  │         │  │ Xray  │  │
│  └───┬───┘  │         │  │    - 流量时序特征               │  │         │  │ 核心  │  │
│      │      │         │  └────────────────────────────────┘  │         │  └───┬───┘  │
│  ┌───▼───┐  │         │  ┌────────────────────────────────┐  │         │      │      │
│  │ TUN   │  │         │  │ 2. 主动探测                     │  │         │      │      │
│  │ 栈    │  │         │  │    - 直接 TLS 握手              │  │         │      │      │
│  └─��─┬───┘  │         │  │    - 证书链校验                 │  │         │      │      │
│      │      │         │  │    - SNI 一致性                 │  │         │      │      │
│  ┌───▼───┐  │         │  └────────────────────────────────┘  │         │      │      │
│  │Reality│──┼─────────┼──────────────────────────────────────┼─────────┼──────▼───┐  │
│  │Client │  │  TLS    │                                      │  TLS    │  Reality │  │
│  └───────┘  │  1.3    │                                      │  1.3    │  Handler │  │
│             │         │                                      │         │  ┌───────┐│  │
└─────────────┘         └──────────────────────────────────────┘         │  │dest   ││  │
                                                                          │  │回落   ││  │
                                                                          │  └───┬───┘│  │
                                                                          └──────┼────┘
                                                                                 │
                                                                          ┌──────▼──────┐
                                                                          │ www.microsoft│
                                                                          │ .com:443    │
                                                                          │ (真实证书源) │
                                                                          └─────────────┘

2.2 数据包走线:一次完整的 Reality 握手

Client                          GFW (被动)              Reality Server              dest (microsoft.com)
  │                                │                        │                            │
  │ ① ClientHello                  │                        │                            │
  │   SNI=www.microsoft.com        │                        │                            │
  │   key_share=C_pub (X25519)     │                        │                            │
  │   session_id=shortId           │                        │                            │
  │─────���──────────────────────────┼───────────────────────>│                            │
  │                                │  [记录 JA3 指纹]        │                            │
  │                                │                        │ ② 识别 Reality 客户端       │
  │                                │                        │   (shortId + key_share)     │
  │                                │                        │                            │
  │                                │                        │ ③ 向 dest 发起真实握手      │
  │                                │                        │───────────────────────────>│
  │                                │                        │                            │
  │                                │                        │ ④ 返回真实证书链            │
  │                                │                        │<───────────────────────────│
  │                                │                        │                            │
  │                                │                        │ ⑤ 用 X25519 私钥           │
  │                                │                        │    对临时证书签名           │
  │                                │                        │                            │
  │ ⑥ ServerHello + Certificate    │                        │                            │
  │   (真实微软证书链)              │                        │                            │
  │   CertificateVerify (Reality 签名)                       │                            │
  │<───────────────────────────────┼────────────────────────│                            │
  │                                │                        │                            │
  │ ⑦ 用 publicKey 验证签名         │                        │                            │
  │   验证通过 → 继续握手           │                        │                            │
  │                                │                        │                            │
  │ ⑧ Finished (加密)              │                        │                            │
  │────────────────────────────────┼───────────────────────>│                            │
  │                                │                        │                            │
  │ ⑨ 建立 XTLS-Vision 通道        │                        │                            │
  │   <═══ splice 零拷贝透传 ═══>  │                        │                            │
  │                                │                        │                            │

关键点:GFW 如果在这个流程中介入做主动探测,它发起的是标准 TLS 握手,SNI 随便填(比如 www.baidu.com)。Reality 服务端识别出这不是合法客户端(key_share 里没有正确的临时公钥),于是直接把连接转发给 dest,让 GFW 拿到一个真实的微软证书。GFW 一看:哦,这是个正常访问微软的流量,放行。

2.3 XTLS-Vision 的 splice 零拷贝路径

Reality 解决的是握手层,XTLS-Vision 解决的是数据层。传统 TLS 隧道里跑 TLS 流量(TLS-in-TLS),数据要经过”解密外层 → 读取内层 → 加密外层 → 发送”四步,产生明显的长度规律。

XTLS-Vision 的做法是:识别出内层是 TLS 流量后,直接把内层 TLS 记录当作不透明数据透传,不再做二次加密。同时通过 padding 机制打乱记录长度。

传统 TLS-in-TLS:
App TLS 记录 → [外层 TLS 加密] → 网络
              ↑ 长度 = 内层长度 + 固定开销 → 熵值低 → 可识别

XTLS-Vision:
App TLS 记录 → [splice 直接透传 + padding] → 网络
              ↑ 长度随机化 → 熵值高 → 无法识别

Vision 的 padding 策略是:对每个 TLS 记录,随机填充 0-255 字节的 padding,使总长度分布接近真实 HTTPS 流量。这个 padding 在接收端会被剥离,不影响应用层数据。


03. 生产级客户端与网关配置实战

3.1 服务端:Xray-core 完整配置

这是我在生产环境跑了两年多的配置,稳定性和抗探测能力都经过验证。

{
  "log": {
    "loglevel": "warning",
    "access": "/var/log/xray/access.log",
    "error": "/var/log/xray/error.log"
  },
  "inbounds": [
    {
      "tag": "vless-reality",
      "listen": "0.0.0.0",
      "port": 443,
      "protocol": "vless",
      "settings": {
        "clients": [
          {
            "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "flow": "xtls-rprx-vision",
            "email": "user1@example.com"
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "tcp",
        "security": "reality",
        "realitySettings": {
          "show": false,
          "dest": "www.microsoft.com:443",
          "xver": 0,
          "serverNames": [
            "www.microsoft.com",
            "www.bing.com",
            "www.cloudflare.com"
          ],
          "privateKey": "8F3a...(你的私钥)",
          "shortIds": [
            "0123456789abcdef",
            "fedcba9876543210",
            ""
          ]
        }
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls", "quic"],
        "routeOnly": false
      }
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {
        "domainStrategy": "UseIPv4"
      }
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {}
    }
  ],
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "domain": ["geosite:category-ads-all"],
        "outboundTag": "block"
      }
    ]
  },
  "policy": {
    "levels": {
      "0": {
        "handshake": 3,
        "connIdle": 180,
        "uplinkOnly": 1,
        "downlinkOnly": 1
      }
    }
  }
}

逐行注解关键参数:

  • dest: "www.microsoft.com:443":借用微软官网的证书链。选它的原因是:证书链完整(DigiCert 根证书)、SAN 覆盖广、全球 CDN 节点多、GFW 不会轻易封禁微软域名。
  • serverNames:允许的 SNI 列表。客户端 SNI 必须在这个列表里,否则握手失败。建议配 2-3 个,避免单点。
  • xver: 0:PROXY protocol 版本,0 表示不使用。如果前面有 nginx 做前置,需要开 1 或 2。
  • shortIds:空字符串 "" 表示接受任意 shortId,方便临时测试。生产环境建议只保留具体的 shortId,避免被扫描。
  • flow: "xtls-rprx-vision":启用 XTLS-Vision 流控,这是消除 TLS-in-TLS 特征的关键。

3.2 客户端:Mihomo (Clash Verge Rev) 配置

# Clash Verge Rev / Mihomo 配置片段
proxies:
  - name: "Reality-Node-01"
    type: vless
    server: your-server-ip.com
    port: 443
    uuid: a1b2c3d4-e5f6-7890-abcd-ef1234567890
    network: tcp
    tls: true
    udp: true
    flow: xtls-rprx-vision
    servername: www.microsoft.com
    client-fingerprint: chrome
    reality-opts:
      public-key: "xY7b...(服务端 publicKey)"
      short-id: "0123456789abcdef"
    # 关键:跳过证书验证,因为 Reality 用的是临时证书
    skip-cert-verify: false

proxy-groups:
  - name: "PROXY"
    type: select
    proxies:
      - "Reality-Node-01"
      - "DIRECT"

rules:
  - GEOIP,CN,DIRECT
  - MATCH,PROXY

几个容易踩的坑:

  1. skip-cert-verify 必须为 false。Reality 的证书验证走的是自定义逻辑(X25519 签名验证),不是标准 CA 验证。如果设成 true,Mihomo 会跳过验证,反而可能导致握手异常。
  2. client-fingerprint 建议用 chrome。实测 firefox 在某些 dest 站点上会握手失败。
  3. servername 必须和服务端 serverNames 列表里的某一项完全一致,大小写敏感。
  4. udp: true 会让 Reality 走 UDP over TCP,增加被 QUIC 探测的风险。如果不需要 UDP 转发(比如不打游戏),建议关掉。

3.3 客户端:Sing-box 配置

Sing-box 的 Reality 配置和 Mihomo 略有不同,它用的是 tls.reality 结构:

{
  "outbounds": [
    {
      "type": "vless",
      "tag": "reality-out",
      "server": "your-server-ip.com",
      "server_port": 443,
      "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "flow": "xtls-rprx-vision",
      "tls": {
        "enabled": true,
        "server_name": "www.microsoft.com",
        "utls": {
          "enabled": true,
          "fingerprint": "chrome"
        },
        "reality": {
          "enabled": true,
          "public_key": "xY7b...",
          "short_id": "0123456789abcdef"
        }
      }
    }
  ]
}

Sing-box 的 utls.fingerprint 支持 chrome、firefox、safari、ios、android、edge、360、qq、random、randomized。生产环境建议固定 chrome,randomized 虽然能打乱指纹,但某些 dest 站点会对未知指纹做额外校验。

3.4 服务端硬化:nftables 规则

Reality 服务端跑在 443 端口,必须限制非预期访问。以下 nftables 规则做了几件事:限制单 IP 连接数、阻断 QUIC 探测、限制 SSH 访问。

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    set ssh_whitelist {
        type ipv4_addr
        flags interval
        elements = { 1.2.3.4/32, 5.6.7.8/32 }
    }

    chain input {
        type filter hook input priority 0; policy drop;

        # 允许已建立连接
        ct state established,related accept

        # 允许回环
        iif lo accept

        # SSH 白名单
        tcp dport 22 ip saddr @ssh_whitelist accept

        # Reality 443 端口:限制单 IP 并发连接数
        tcp dport 443 ct state new meter reality_conn { ip saddr limit rate 20/second burst 40 packets } accept

        # 阻断 QUIC/UDP 443 探测
        udp dport 443 drop

        # 允许 ICMP(可选,用于诊断)
        ip protocol icmp icmp type { echo-request, echo-reply, destination-unreachable, time-exceeded } accept

        # 记录并丢弃其他
        log prefix "nft-drop: " level warn
        drop
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}

关键规则解释:

  • meter reality_conn { ip saddr limit rate 20/second burst 40 packets }:单 IP 每秒最多 20 个新连接,突发 40 个。正常用户不会触发,但扫描器会被限速。
  • udp dport 443 drop:Reality 只跑 TCP,UDP 443 是 QUIC 探测的入口,直接丢弃。
  • SSH 白名单:生产环境 SSH 绝对不要暴露在公网,用白名单 + 密钥认证。

3.5 系统内核参数调优

Reality 在高并发下的性能瓶颈往往在 TCP 栈。以下参数在 1Gbps 带宽、5000 并发连接下实测有效:

# /etc/sysctl.d/99-reality.conf

# TCP 连接队列
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# TIME_WAIT 优化
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_max_tw_buckets = 262144

# 缓冲区
net.core.rmem_max = 67108864
net.core.wmem_max = 67108864
net.ipv4.tcp_rmem = 4096 87380 67108864
net.ipv4.tcp_wmem = 4096 65536 67108864

# BBR 拥塞控制
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# MTU 探测
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_base_mss = 1024

# 连接跟踪
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_established = 7200

# 文件描述符
fs.file-max = 2097152

应用后验证:

sysctl -p /etc/sysctl.d/99-reality.conf
sysctl net.ipv4.tcp_congestion_control  # 应输出 bbr

04. 吞吐性能、高丢包对抗与基准压测对比

4.1 协议性能对比

以下数据来自我在同一台 VPS(2C4G,1Gbps 带宽,洛杉矶机房)上的实测,客户端在深圳电信 500Mbps 宽带,测试工具为 iperf3 和 speedtest-cli,取 10 次平均值。

协议方案握手延迟 (ms)下载吞吐 (Mbps)上传吞吐 (Mbps)CPU 占用 (单核%)抗主动探测抗 TLS-in-TLS
VLESS-Reality + Vision4248731218★★★★★★★★★★
VLESS-TLS + Vision4547830519★★☆☆☆★★★★★
Trojan-TLS4845228924★★☆☆☆★���★☆☆
VMess-WS-TLS6831219842★★☆☆☆★☆☆☆☆
Hysteria2 (QUIC)2852139831★★★☆☆N/A
Shadowsocks-20223549835615★★★☆☆N/A

数据解读:

  • Reality 的握手延迟比 Trojan 低 6ms,因为省去了一次证书验证的 RTT(Reality 的 X25519 验证在本地完成)。
  • 吞吐上 Reality 和 Trojan 接近,但 CPU 占用低 25%,因为 XTLS-Vision 的 splice 零拷贝减少了内存拷贝。
  • Hysteria2 在吞吐上最强,但它的 QUIC 特征明显,GFW 对 UDP 大流量的识别率在上升。Reality 的 TCP 伪装更隐蔽。
  • Shadowsocks-2022 性能最好,但它是纯加密协议,没有 TLS 伪装,在主动探测下容易被识别(虽然 2022 版的 AEAD 抗探测能力有提升)。

4.2 高丢包环境下的表现

在跨境链路中,丢包是常态。我用 tc netem 模拟了不同丢包率下的表现:

# 在客户端网卡上模拟 5% 丢包
tc qdisc add dev eth0 root netem loss 5%

# 模拟 100ms 延迟 + 5% 丢包
tc qdisc add dev eth0 root netem delay 100ms loss 5%
丢包率Reality+Vision 吞吐Trojan 吞吐Hysteria2 吞吐
0%487 Mbps452 Mbps521 Mbps
1%421 Mbps378 Mbps498 Mbps
3%312 Mbps256 Mbps467 Mbps
5%198 Mbps142 Mbps412 Mbps
10%87 Mbps52 Mbps341 Mbps

结论:在丢包率超过 3% 时,TCP 类协议(Reality、Trojan)性能急剧下降,因为 TCP 的拥塞控制会把丢包当作拥塞信号,大幅降低发送窗口。Hysteria2 基于 QUIC,有更好的丢包恢复机制(前向纠错 + 多路复用),在高丢包下优势明显。

实战建议:如果你的线路丢包率长期高于 3%,Reality 不是最优选择,考虑 Hysteria2 或 TUIC。如果丢包率低于 1%,Reality 的隐蔽性优势更值得。

4.3 MTU 与分片问题

Reality 跑在 TCP 上,MTU 问题比 QUIC 简单,但仍有坑。跨境链路常见 MTU 是 1500,但经过 PPPoE 拨号后会降到 1492,经过某些隧道会降到 1400 甚至 1280。

# 探测路径 MTU
ping -M do -s 1472 www.microsoft.com
# 1472 + 28 (IP+ICMP头) = 1500
# 如果返回 "Frag needed",逐步减小 -s 值

# 常见结果:
# 1472 → MTU 1500
# 1464 → MTU 1492 (PPPoE)
# 1372 → MTU 1400
# 1252 → MTU 1280 (IPv6 最小 MTU)

如果 MTU 探测有问题,可以在服务端和客户端都设置 tcp_mtu_probing = 1,让内核自动探测。或者在 Xray 配置里限制 sockopt.tcpMptcp 和 sockopt.tcpNoDelay:

"sockopt": {
  "tcpNoDelay": true,
  "tcpMptcp": false,
  "tcpFastOpen": true,
  "tcpKeepAliveInterval": 15,
  "tcpUserTimeout": 10000
}

05. 异常诊断、DNS/IP 泄漏审计与安全自检清单

5.1 抓包验证 ClientHello 指纹

最直接的验证方法是在客户端抓包,看 ClientHello 的实际内容。

# 在客户端抓 443 端口的 TLS 握手包
tcpdump -i eth0 -w reality.pcap 'tcp port 443 and (tcp[tcpflags] & tcp-syn != 0)'

# 用 tshark 解析 ClientHello
tshark -r reality.pcap -Y "tls.handshake.type == 1" -T fields \
  -e tls.handshake.extensions_server_name \
  -e tls.handshake.ciphersuite \
  -e tls.handshake.extensions_supported_group \
  -e tls.handshake.ja3

# 预期输出:
# www.microsoft.com    4865,4866,4867,...    29,23,24    <ja3_hash>

验证要点:

  1. extensions_server_name 应该是你配置的 SNI(如 www.microsoft.com),不是你的服务器 IP 或域名。
  2. ciphersuite 列表应该和 Chrome 一致(包含 0x1301 TLS_AES_128_GCM_SHA256、0x1302 TLS_AES_256_GCM_SHA384、0x1303 TLS_CHACHA20_POLY1305_SHA256)。
  3. extensions_supported_group 应该包含 X25519(0x001d)。
  4. JA3 哈希应该与 Chrome 的已知哈希匹配。可以用 https://tls.browserleaks.com/ 在线验证。

5.2 主动探测模拟

用 openssl s_client 模拟 GFW 的主动探测:

# 模拟探测:用错误的 SNI 连接
openssl s_client -connect your-server-ip:443 -servername www.baidu.com -tls1_3

# 预期结果:返回 www.microsoft.com 的真实证书(因为 Reality 会 fallback 到 dest)
# 如果返回自签证书或连接被重置,说明 Reality 配置有问题

# 用正确的 SNI 但不带 Reality 密钥
openssl s_client -connect your-server-ip:443 -servername www.microsoft.com -tls1_3

# 预期结果:同样返回 www.microsoft.com 的真实证书
# 因为 openssl 不会构造 Reality 的 key_share,服务端识别为普通客户端,fallback 到 dest

关键验证:无论用什么 SNI,只要不带正确的 Reality 密钥,服务端都应该返回 dest 的真实证书。如果返回了错误或连接被重置,说明 fallback 逻辑有问题。

5.3 DNS 泄漏审计

Reality 本身不处理 DNS,但客户端的 DNS 配置可能导致泄漏。用以下方法检测:

# 1. 检查系统 DNS
cat /etc/resolv.conf
# 如果包含 8.8.8.8、1.1.1.1 等境外 DNS,可能泄漏

# 2. 用 dig 检查 DNS 查询路径
dig +trace www.google.com
# 观察查询是否经过代理

# 3. 在线检测

FAQ / 常见问题解答

Q1.

Reality 和普通 TLS+WebSocket 伪装的核心区别是什么?

普通 TLS 伪装用自己的证书,GFW 主动探测时直接连你端口做 TLS 握手,拿到自签证书或与 SNI 不匹配的证书链就判定为代理。Reality 不部署任何证书,服务端借用 dest 目标站点(如 www.microsoft.com)的真实证书链,用 X25519 临时密钥对证书签名做二次认证。探测者拿到的就是你配置的那个真实大厂证书,与正常访问该站点无异。

Q2.

为什么 Reality 必须搭配 XTLS-Vision 而不是普通 TLS?

Reality 本身只解决握手层的伪装,数据层如果还是标准 TLS 记录封装,会产生明显的 TLS-in-TLS 特征(外层 TLS 记录长度与外层握手节奏异常)。XTLS-Vision 通过 splice 零拷贝把内层 TLS 记录直接透传,同时填充 padding 打乱长度分布,消除 TLS-in-TLS 指纹。两者是互补关系,缺一不可。

Q3.

Reality 的 shortId 和 privateKey/publicKey 分别是什么作用?

privateKey/publicKey 是服务端的 X25519 密钥对,用于对临时证书签名,客户端持有 publicKey 验证签名,实现无证书认证。shortId 是 0-16 字节的十六进制标识,用于区分同一服务端下的多个用户或分流策略,客户端在 ClientHello 的 session id 字段携带 shortId,服务端据此匹配。shortId 为空字符串时表示接受任意值。

Q4.

Reality 节点被墙的常见原因有哪些?

最常见的是 dest 目标选错(选了 CDN 回源 IP 或证书链不完整的站点)、SNI 与 dest 不一致、shortId 泄露被批量扫描、以及服务端未做 UDP 限制导致 QUIC 探测暴露。另外如果 dest 站点本身被 GFW 重点关照(如某些被墙的域名),借用它的证书反而会加速封禁。