海外机场 LIVE
protocols Informational · 作者: 海外机场技术工程组 · (更新于: 2026年10月2日)

Reality 协议底层深度剖析:从 TLS 1.3 握手、JA3/JA4 客户端指纹到 SNI 偷取伪装实战

Reality 的核心结论是:它不依赖自有域名与自签名证书,而是通过偷取真实高信誉站点的 SNI,在 TLS 1.3 握手中借用其证书链完成认证,使主动探测者无法通过证书异常识别代理。其抗封锁能力取决于三点:ClientHello 指纹与真实浏览器一致、Target SNI 站点支持 TLS 1.3 与 OCSP Stapling、服务端回落行为与真实站点不可区分。仅靠协议本身不足以防封,必须配合 JA3/JA4 指纹伪装与合规 Target 选择。

快速答案与核心结论

Reality 的核心结论是:它不依赖自有域名与自签名证书,而是通过偷取真实高信誉站点的 SNI,在 TLS 1.3 握手中借用其证书链完成认证,使主动探测者无法通过证书异常识别代理。其抗封锁能力取决于三点:ClientHello 指纹与真实浏览器一致、Target SNI 站点支持 TLS 1.3 与 OCSP Stapling、服务端回落行为与真实站点不可区分。仅靠协议本身不足以防封,必须配合 JA3/JA4 指纹伪装与合规 Target 选择。

Reality 协议底层深度剖析:从 TLS 1.3 握手、JA3/JA4 客户端指纹到 SNI 偷取伪装实战

引言

Reality 是 Xray-core 在 2023 年引入的一种新型 TLS 伪装方案,其设计目标直指传统 TLS 伪装方案(WebSocket+TLS、Trojan、gRPC+TLS)的三个结构性缺陷:自有域名暴露、证书链可追溯、主动探测可识别。理解 Reality 必须建立在对 TLS 1.3 ClientHello 二进制结构、JA3/JA4 指纹算法、以及 X25519 密钥交换的精确掌握之上。本文前三章从协议栈最底层开始,逐字节拆解握手报文,给出可落地的 Target 挑选与校验方法。


1. TLS 1.3 握手与 JA3/JA4 指纹生成机制

1.1 ClientHello 结构逐字段解析

TLS 1.3(RFC 8446)的 ClientHello 是明文传输的,这是所有指纹技术的物理基础。整个记录层结构如下:

TLS Record Header (5 bytes)
├── ContentType      = 0x16 (Handshake)
├── legacy_version   = 0x0301 (TLS 1.0, 兼容性占位)
└── length           = 2 bytes

Handshake Header (4 bytes)
├── HandshakeType    = 0x01 (ClientHello)
└── length           = 3 bytes

ClientHello Body
├── legacy_version          = 0x0303 (TLS 1.2, 固定值)
├── random[32]              = 4 bytes 时间戳 + 28 bytes 随机数
├── legacy_session_id       = 变长 (0~32 bytes, 中间盒兼容)
├── cipher_suites           = 变长列表
├── legacy_compression      = 0x01 0x00 (仅 null)
└── extensions              = 变长列表 ← 指纹核心

legacy_version 字段在 TLS 1.3 中被固定为 0x0303,真实版本协商通过 supported_versions 扩展完成。legacy_session_id 在 TLS 1.3 中承担中间盒兼容职责,Chrome 自 110 版本起填充 32 字节随机值,Firefox 保持空值,这个字段的差异直接进入 JA3 的扩展拼接序列之前的指纹空间。

cipher_suites 的顺序在指纹识别中权重极高。Chrome 的典型序列以 0x1301 (TLS_AES_128_GCM_SHA256) 开头,紧随 0x1302、0x1303,然后才是 0xc02b、0xc02f 等 TLS 1.2 套件;Firefox 则把 0x1303 (ChaCha20) 放在首位(因为 Mozilla 在无 AES-NI 硬件上优先 ChaCha20)。顺序的差异在 JA3 中直接体现为 MD5 输入串的不同。

extensions 列表的顺序和内容是指纹的决定性因素。Chrome 的扩展顺序(截至 124 版本)大致为:

0x0000 server_name
0x0017 extended_master_secret
0x000d signature_algorithms
0x0010 application_layer_protocol_negotiation
0x000b ec_point_formats
0x0023 session_ticket
0x0012 signed_certificate_timestamp
0x002b supported_versions
0x0033 key_share
0x002d psk_key_exchange_modes
0x0029 pre_shared_key
0x000a supported_groups
0x001c record_size_limit
0x002a early_data
...

扩展顺序在 RFC 中并无强制约束,但真实浏览器实现由代码路径决定,顺序高度稳定。uTLS 伪造失败的最常见原因就是扩展顺序与目标浏览器不一致,即便扩展集合完全相同,顺序错位也会导致 JA3 哈希完全不同。

1.2 JA3 指纹算法

JA3(Salesforce 2017 年提出)的定义为:

JA3_string = SSLVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats
JA3_hash   = MD5(JA3_string)

各字段拼接规则:

  • SSLVersion:ClientHello 的 legacy_version,TLS 1.3 场景下恒为 771(0x0303)
  • Ciphers:cipher_suites 中每个套件以十进制表示,用 - 连接,GREASE 值被剔除
  • Extensions:extensions 中每个扩展类型以十进制表示,用 - 连接,GREASE 剔除
  • EllipticCurves:supported_groups 扩展内的曲线 ID,十进制,- 连接
  • EllipticCurvePointFormats:ec_point_formats 扩展内的格式值

GREASE(RFC 8701)是 Google 提出的抗 ossification 机制,在 0x0a0a 到 0xfafa 之间以 0x?a?a 模式插入占位值。JA3 算法在计算前必须剔除 GREASE,否则同一客户端每次连接指纹都会变化。

JA3 的已知碰撞缺陷有两类。第一类是顺序无关性缺失:JA3 保留了 cipher 和 extension 的顺序信息,但丢弃了扩展内部的参数内容。例如 signature_algorithms 扩展的具体签名算法列表、supported_versions 的具体版本号,都不进入 JA3 字符串。这意味着两个扩展集合相同但内部参数不同的客户端会产生相同 JA3。第二类是GREASE 剔除带来的信息损失:剔除 GREASE 后,Chrome 与某些 uTLS 模板的指纹可能碰撞。

生产环境中的 JA3 数据库(如 Salesforce 的公开数据集)显示,Chrome 的 JA3 哈希在 2024 年仍稳定在 cd08e31494f9531f560d64c695473da9 附近,但每次 Chrome 大版本更新都会漂移。

1.3 JA4 指纹体系

JA4(FoxIO 2023 年提出)针对 JA3 的缺陷重新设计,采用三段式结构:

JA4_a = [protocol][version][SNI][cipher_count][extension_count][ALPN]
JA4_b = SHA256(sorted_cipher_suites)[:12]
JA4_c = SHA256(sorted_extensions + signature_algorithms)[:12]
JA4   = JA4_a + "_" + JA4_b + "_" + JA4_c

具体编码规则:

  • protocol:t 表示 TCP,q 表示 QUIC
  • version:13 表示 TLS 1.3,12 表示 TLS 1.2
  • SNI:d 表示携带 SNI 且为域名,i 表示携带 SNI 且为 IP,n 表示无 SNI
  • cipher_count:两位十进制,超过 99 记为 99
  • extension_count:两位十进制
  • ALPN:h2 取首尾字符 h2,http/1.1 取 h1,无 ALPN 取 00

JA4_b 和 JA4_c 使用排序后的列表做 SHA256,这消除了顺序敏感性,同时把 signature_algorithms 纳入哈希,大幅提升了对 uTLS 伪装的识别精度。uTLS 若只复制扩展顺序而未复制签名算法列表,JA4_c 会立即暴露。

JA4 的工程价值在于:JA4_a 是明文可读的,运维可以直接在日志中 grep t13d1516h2 这类前缀快速筛选客户端类型,无需查表。JA4_b/c 的哈希碰撞概率在 48 bit 截断下对普通流量足够,但对大规模僵尸网络仍有理论碰撞空间。

1.4 uTLS 指纹伪造原理

uTLS 是 refraction-networking 维护的 Go 库,通过替换 crypto/tls 的 ClientHello 构造逻辑实现指纹伪造。核心接口是 ClientHelloID,内置模板包括 HelloChrome_120、HelloFirefox_120、HelloSafari_16_0 等。

伪造流程:

1. 加载模板 → 得到 (cipher_suites, extensions, curves, point_formats) 四元组
2. 应用 GREASE 注入策略(Chrome 模板会随机插入 GREASE)
3. 构造 ClientHello 字节流,覆盖默认实现
4. 交由底层 TLS 栈完成密钥交换

生产环境中的关键 Trade-off:uTLS 模板与真实浏览器存在版本滞后。Chrome 每 4 周发布一个大版本,uTLS 模板通常滞后 2~6 周。在这段窗口期内,使用旧模板的客户端 JA4 与真实 Chrome 不一致,会被高级 DPI 系统标记。Reality 的设计部分规避了这个问题:它不追求指纹与浏览器一致,而是追求握手在密码学层面与真实 Target 站点不可区分。


2. Reality 与传统 TLS 伪装的架构本质差异

2.1 传统方案的共性缺陷

WebSocket+TLS 与 Trojan 的架构可以抽象为:

Client ──TLS(SNI=yourdomain.com)──> Server ──> 内网代理
                │
                └── 证书: Let's Encrypt / 自签

这套架构有三个可被利用的探测点:

证书透明度日志(CT Log):Let’s Encrypt 签发的证书全部进入 CT Log,任何机构可以查询 yourdomain.com 的证书签发记录。GFW 的主动探测系统会定期扫描 CT Log 新增域名,对可疑域名发起 TLS 握手探测。

主动探测识别:探测者用任意 SNI 连接服务器,若服务器返回有效证书并完成握手,但后续 HTTP 请求返回异常响应(如 400、空响应、非标准 HTTP 头),则判定为代理服务器。Trojan 的应对是回落至 Nginx,但 Nginx 的响应特征(Server 头、TLS 指纹)与真实站点仍有差异。

证书链特征:自签证书的 Issuer 字段、有效期、扩展字段与商业 CA 差异明显;Let’s Encrypt 证书的 OCSP 响应由 LE 的 OCSP 服务器签发,与目标伪装站点不匹配。

2.2 Reality 的无域名架构

Reality 的核心设计是不持有域名、不申请证书。服务端在握手阶段借用 Target 站点的真实证书链:

Client                          Reality Server                    Target (e.g. www.microsoft.com)
  │                                  │                                    │
  │──ClientHello(SNI=www.microsoft.com, key_share=X25519_pub_C)──────────>│
  │                                  │                                    │
  │                                  │──ClientHello(SNI=www.microsoft.com)─>│
  │                                  │<──ServerHello + Cert + CertVerify────│
  │                                  │                                    │
  │<──ServerHello(临时证书, 私钥由 X25519 派生)────────────────────────────│
  │                                  │                                    │
  │──Finished──────────────────────>│                                    │
  │<──Finished───────────────────────│                                    │
  │                                  │                                    │
  │──应用数据(Reality 认证头)────────>│                                    │

服务端在收到 ClientHello 后,用 Target 的真实地址发起一次 TLS 握手,获取 Target 的证书链。然后用 X25519 派生的临时密钥对证书进行”重新签名”,实际上 Reality 使用了一种基于 X25519 的临时证书机制,服务端持有 Target 的公钥(预配置),客户端在 ClientHello 的 key_share 中嵌入认证信息。

2.3 认证机制

Reality 的认证基于 X25519 临时密钥与 Target 公钥的派生:

客户端:
  ephemeral_priv_C, ephemeral_pub_C = X25519_keygen()
  shared = X25519(ephemeral_priv_C, target_pub)
  auth_key = HKDF(shared, "REALITY", 32)
  在 ClientHello 的 session_id 字段嵌入 auth_key 的加密形式

服务端:
  target_priv (预配置)
  shared = X25519(target_priv, ephemeral_pub_C)  ← 与客户端相同
  auth_key = HKDF(shared, "REALITY", 32)
  校验 session_id 中的认证信息

认证通过后,服务端接管连接,使用临时证书完成 TLS 握手。认证失败则透明转发至 Target 站点。

2.4 回落行为设计

未通过认证的探测请求被透明转发至真实 Target 站点:

Prober ──ClientHello(SNI=www.microsoft.com)──> Reality Server
                                                    │
                                                    │ 认证失败
                                                    ↓
                                              TCP 转发至 www.microsoft.com:443
                                                    │
                                              <──真实 ServerHello + Cert──
                                                    │
Prober <──真实 Microsoft 证书 + 完整握手─────────────────

探测者得到的是 Microsoft 的真实证书和真实响应,无法区分这是 Microsoft 服务器还是 Reality 服务器。这是 Reality 相对于 Trojan 的根本性优势:回落目标不是本地 Nginx,而是真实的外部站点。

工程 Trade-off:回落转发引入额外 RTT。若 Reality 服务器与 Target 站点物理距离远(如国内服务器回落至美国 Microsoft),探测者会观察到异常高的握手延迟,这可能成为新的指纹。生产环境应选择地理邻近的 Target。


3. Target SNI 挑选实战与合规性校验

3.1 Target 站点硬性条件

Reality 服务端对 Target 的要求:

条件原因
支持 TLS 1.3Reality 握手依赖 TLS 1.3 的 key_share 扩展
支持 X25519Reality 认证基于 X25519 密钥交换
启用 OCSP Stapling证书链完整性,避免客户端额外查询 OCSP
ALPN 含 h2 与 http/1.1指纹一致性,避免 ALPN 不匹配暴露
证书有效期 > 90 天减少证书轮换导致的可用性中断
无 CDN 前置或 CDN 支持 TLS 1.3CDN 可能修改握手特征

不满足 X25519 的站点(如仅支持 P-256 的旧站点)无法作为 Target。OCSP Stapling 缺失会导致客户端在握手后额外发起 OCSP 查询,产生可观测的流量特征。

3.2 CDN 节点合规性评估

Cloudflare:默认支持 TLS 1.3、X25519、OCSP Stapling,ALPN 含 h2。但 Cloudflare 的握手特征高度统一,大量 Reality 服务器选择 Cloudflare 前置的站点作为 Target,导致这些 SNI 的流量模式异常集中。GFW 已对部分 Cloudflare IP 段实施 SNI 白名单策略。

Akamai:TLS 1.3 支持良好,但 Akamai 的边缘节点会根据客户端地理位置返回不同证书,Reality 服务端若与客户端地理距离远,可能获取到与客户端预期不同的证书链。

Fastly:支持完整,但 Fastly 的 TLS 终止节点较少,Target 选择范围有限。

国内 CDN:阿里云 CDN、腾讯云 CDN 部分节点仍以 TLS 1.2 为主,且 OCSP Stapling 支持不完整。国内 CDN 的握手特征与海外差异明显,作为 Target 会引入额外指纹。

生产环境推荐:选择地理邻近、TLS 1.3 支持完整、流量规模大的站点。流量规模大意味着 Reality 流量可以隐藏在正常流量中。

3.3 SNI 偷取风险

Target 被墙:若 Target 站点本身被 GFW 封锁,Reality 服务器的回落行为会失败,探测者观察到连接超时,暴露服务器异常。

证书轮换:Target 站点更换证书后,Reality 服务端缓存的证书链失效,需要重新获取。Xray-core 实现了证书自动刷新,但刷新窗口期内可能出现握手失败。

IP 封禁:Reality 服务器 IP 若被 GFW 封禁,所有 Target 选择都失效。这是不可规避的风险,只能通过 IP 轮换缓解。

传导影响可以量化为可用性公式:

$$A = A_{target} \times A_{cert} \times A_{ip}$$

其中 $A_{target}$ 为 Target 站点可用性,$A_{cert}$ 为证书链有效性,$A_{ip}$ 为服务器 IP 可用性。三个因子任一为 0,整体可用性为 0。

3.4 自动化校验脚本

使用 openssl s_client 批量检测 Target 的 TLS 1.3 支持度:

#!/bin/bash
# reality-target-check.sh
# 批量检测候选 Target 站点的 TLS 1.3 / X25519 / OCSP Stapling 支持情况
# 依赖: openssl 1.1.1+, jq

TARGETS=(
    "www.microsoft.com:443"
    "www.apple.com:443"
    "www.cloudflare.com:443"
    "www.amazon.com:443"
)

for target in "${TARGETS[@]}"; do
    host="${target%%:*}"
    port="${target##*:}"
    echo "=== Checking $host:$port ==="

    # 1. 检测 TLS 1.3 支持
    tls13=$(echo | openssl s_client -connect "$target" -tls1_3 -servername "$host" 2>/dev/null | grep -c "TLSv1.3")
    echo "  TLS 1.3: $([ "$tls13" -gt 0 ] && echo YES || echo NO)"

    # 2. 检测 X25519 密钥交换
    # openssl s_client -groups 参数指定曲线,若握手成功则支持
    x25519=$(echo | openssl s_client -connect "$target" -tls1_3 -groups X25519 -servername "$host" 2>/dev/null | grep -c "Server Temp Key: X25519")
    echo "  X25519: $([ "$x25519" -gt 0 ] && echo YES || echo NO)"

    # 3. 检测 OCSP Stapling
    ocsp=$(echo | openssl s_client -connect "$target" -tls1_3 -status -servername "$host" 2>/dev/null | grep -c "OCSP Response Status: successful")
    echo "  OCSP Stapling: $([ "$ocsp" -gt 0 ] && echo YES || echo NO)"

    # 4. 检测 ALPN
    alpn=$(echo | openssl s_client -connect "$target" -tls1_3 -alpn h2,http/1.1 -servername "$host" 2>/dev/null | grep "ALPN protocol:")
    echo "  ALPN: $alpn"

    # 5. 证书有效期
    expiry=$(echo | openssl s_client -connect "$target" -servername "$host" 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
    echo "  Cert Expiry: $expiry"

    echo ""
done

使用 tls.peet.ws 检测 JA3/JA4 指纹一致性:

#!/bin/bash
# 通过 tls.peet.ws API 获取当前客户端的 JA3/JA4 指纹
# 用于验证 uTLS 模板是否生效

curl -s https://tls.peet.ws/api/all | jq '{
    ja3: .tls.ja3,
    ja3_hash: .tls.ja3_hash,
    ja4: .tls.ja4,
    akamai: .tls.akamai_hash,
    user_agent: .http_user_agent
}'

3.5 Reality 服务端配置示例

Xray-core 的 Reality 服务端配置(config.json):

{
  "inbounds": [
    {
      "listen": "0.0.0.0",
      "port": 443,
      "protocol": "vless",
      "settings": {
        "clients": [
          {
            "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
            "flow": "xtls-rprx-vision"
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "tcp",
        "security": "reality",
        "realitySettings": {
          "show": false,
          "dest": "www.microsoft.com:443",
          "xver": 0,
          "serverNames": [
            "www.microsoft.com",
            "www.apple.com"
          ],
          "privateKey": "YOUR_X25519_PRIVATE_KEY",
          "shortIds": [
            "0123456789abcdef"
          ]
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "tag": "direct"
    }
  ]
}

字段说明:

  • dest:Target 站点地址,Reality 服务端从此处获取证书链
  • serverNames:允许的 SNI 列表,客户端必须使用其中之一
  • privateKey:X25519 私钥,由 xray x25519 命令生成
  • shortIds:短 ID,用于客户端认证的辅助标识,16 进制字符串

生成密钥对:

xray x25519
# Private key: <base64>
# Public key:  <base64>

3.6 客户端配置示例

sing-box 客户端配置:

{
  "inbounds": [
    {
      "type": "tun",
      "tag": "tun-in",
      "inet4_address": "172.19.0.1/30",
      "auto_route": true,
      "strict_route": true,
      "stack": "system"
    }
  ],
  "outbounds": [
    {
      "type": "vless",
      "tag": "reality-out",
      "server": "YOUR_SERVER_IP",
      "server_port": 443,
      "uuid": "b831381d-6324-4d53-ad4f-8cda48b30811",
      "flow": "xtls-rprx-vision",
      "tls": {
        "enabled": true,
        "server_name": "www.microsoft.com",
        "utls": {
          "enabled": true,
          "fingerprint": "chrome"
        },
        "reality": {
          "enabled": true,
          "public_key": "YOUR_X25519_PUBLIC_KEY",
          "short_id": "0123456789abcdef"
        }
      }
    }
  ]
}

3.7 网络层参数与 RTT 计算

Reality 服务器与 Target 站点的物理距离直接影响回落延迟。光纤中光速为:

$$v = \frac{c}{n} \approx \frac{3 \times 10^8}{1.467} \approx 2.045 \times 10^8 \text{ m/s}$$

其中 $n = 1.467$ 为石英光纤在 1550nm 波长的折射率。往返时延:

$$t_{RTT} = \frac{2d \cdot n}{c}$$

例如服务器在上海,Target 在东京,直线距离 $d \approx 1800$ km:

$$t_{RTT} = \frac{2 \times 1.8 \times 10^6 \times 1.467}{3 \times 10^8} \approx 17.6 \text{ ms}$$

实际光纤路径通常为直线的 1.31.5 倍,加上路由跳数,实测 RTT 约 2535 ms。若 Target 选在美西,$d \approx 10000$ km,理论 RTT 约 98 ms,实测 130~160 ms。回落延迟超过 100 ms 会显著增加探测者的异常感知概率。

MTU 方面,Reality 使用 TCP 承载,需考虑 TLS 记录层开销。以太网 MTU 1500,减去 IP 头 20、TCP 头 20、TLS 记录头 5、TLS 内部握手头,有效载荷约 1400 字节。若服务器与客户端之间存在 PPPoE(MTU 1492)或 VPN 隧道(MTU 1400),需相应调整 MSS Clamping:

# iptables MSS Clamping,防止 PMTU 黑洞
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
    -j TCPMSS --clamp-mss-to-pmtu

# 或手动指定 MSS
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
    -j TCPMSS --set-mss 1360

拥塞窗口方面,Reality 握手阶段涉及多个 RTT。TLS 1.3 完整握手为 1-RTT(session resumption 为 0-RTT),加上 Reality 的认证校验,实际握手约 1.5~2 RTT。在 BDP(Bandwidth-Delay Product)较大的链路上,初始拥塞窗口(Linux 默认 initcwnd=10,约 14.6 KB)可能不足以填满管道,导致握手延迟增加。可通过调整 initcwnd 优化:

# 查看当前 initcwnd
ip route show | grep initcwnd

# 设置为 20(约 29 KB)
ip route change default via <gateway> dev eth0 initcwnd 20

工程 Trade-off:提高 initcwnd 会加剧网络拥塞时的丢包,需在延迟敏感与拥塞友好之间权衡。对于 Reality 服务器,建议 initcwnd=20,兼顾握手速度与网络友好性。


(前三章完,后续章节将展开 Reality 的 X25519 认证细节、XTLS Vision 流控机制、以及 GFW 主动探测对抗实战。)


4. sing-box 与 Xray 服务端及客户端配置实录

4.1 Xray 服务端 inbound realitySettings 参数解剖

Reality 服务端的核心配置块落在 inbound.streamSettings.realitySettings。下面这份是生产环境里跑过半年以上的模板,逐字段注释。

{
  "inbounds": [
    {
      "listen": "0.0.0.0",
      "port": 443,
      "protocol": "vless",
      "settings": {
        "clients": [
          { "id": "9f8a...uuid", "flow": "xtls-rprx-vision" }
        ],
        "decryption": "none",
        "fallbacks": [
          { "dest": 8080, "xver": 1 }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "reality",
        "realitySettings": {
          "show": false,
          "dest": "www.microsoft.com:443",
          "xver": 1,
          "serverNames": ["www.microsoft.com", "www.bing.com"],
          "privateKey": "gN3...私钥base64",
          "minClientVer": "",
          "maxClientVer": "",
          "maxTimeDiff": 0,
          "shortIds": ["", "a1b2c3d4", "9f3e7a5b2c1d4e6f"],
          "fingerprint": "chrome",
          "publicKey": "可选,仅客户端需要"
        }
      }
    }
  ]
}

关键字段的实战含义:

字段取值约束现场坑点
desthost:port,必须是真实存在的 TLS 1.3 站点填 IP 会暴露;填 CDN 后端 IP 会因 SNI 不匹配被 403
serverNames与 dest 证书 SAN 匹配的域名数组必须与 dest 的证书域名一致,否则回落握手失败
privateKeyx25519 私钥 base64泄露等于全网节点被识别,务必离线生成
shortIds十六进制字符串,长度偶数,0~16 字节空字符串 "" 是合法的,代表任意 shortId 都接受(不推荐生产)
fingerprintchrome/firefox/safari/random服务端此字段仅用于伪装自身 ClientHello,不影响客户端
maxTimeDiff毫秒,0 表示不校验大于 0 时客户端与服务端时钟漂移超限会直接拒绝

dest 的选择有一条铁律:必须是你能接受”被当作 SNI 偷取目标”的站点。微软、苹果、Cloudflare 系(www.cloudflare.com、www.apple.com、www.microsoft.com、www.amazon.com)是社区公认的低风险目标,因为这些站点的 TLS 指纹稳定、证书链干净、流量巨大,被偷 SNI 很难被察觉。反过来,小站、个人博客、企业内部系统做 Target 是自杀行为,对方一看访问日志里全是无法解释的 TLS 握手,直接封 IP 段。

4.2 sing-box 客户端 outbound reality 配置

sing-box 的 reality outbound 与 Xray 结构不同,重点在 tls.reality 和 utls 的配合。

{
  "outbounds": [
    {
      "type": "vless",
      "tag": "proxy",
      "server": "1.2.3.4",
      "server_port": 443,
      "uuid": "9f8a...uuid",
      "flow": "xtls-rprx-vision",
      "tls": {
        "enabled": true,
        "server_name": "www.microsoft.com",
        "utls": {
          "enabled": true,
          "fingerprint": "chrome"
        },
        "reality": {
          "enabled": true,
          "public_key": "服务端公钥base64",
          "short_id": "a1b2c3d4"
        }
      }
    }
  ]
}

三个字段的对应关系必须严格一致:

  1. server_name 必须落在服务端 serverNames 数组内,否则 Reality 握手第一步就被拒。
  2. utls.fingerprint 决定 ClientHello 的指纹模板。服务端不校验这个字段,但中间设备会看。用 chrome 就要保证 TCP 栈行为(窗口缩放、TFO、ALPN)也接近 Chrome,否则 JA3 与 TCP 指纹矛盾,会被高级 DPI 标记。
  3. short_id 必须命中服务端 shortIds 列表。空 short_id 匹配服务端空字符串项。

flow 字段在 VLESS + Reality 下推荐 xtls-rprx-vision,它把内层流量与 TLS 记录层做零拷贝拼接,显著降低 CPU。若客户端不支持 Vision(老版本内核),退化为空字符串,走普通 VLESS 封装。

4.3 密钥与 shortId 生成流程

x25519 密钥对生成:

# Xray 自带命令
xray x25519
# 输出示例
Private key: gN3kQ...(base64)
Public key:  H8xLp...(base64)

privateKey 只放服务端,publicKey 只放客户端。这两个值不能互换,公钥泄露无风险,私钥泄露是灾难。

shortId 随机化策略,多人多节点场景下必须规划:

# 生成 8 字节 shortId(16 位十六进制)
openssl rand -hex 8
# 生成 4 字节短 shortId
openssl rand -hex 4

分配原则:

  • 单节点多用户:每个用户一个独立 shortId,服务端 shortIds 数组里全部列出。这样单个用户 shortId 泄露不影响其他用户。
  • 多节点冗余:同一用户在不同节点使用不同 shortId,避免节点间关联分析。
  • 禁止把 shortId 当作密码复用。shortId 只有 8 字节空间,碰撞概率虽低但可枚举,配合 UUID 才是真正的认证。

4.4 流量控制与回落

flow: xtls-rprx-vision 是 Reality 的性能核心。它让内层明文直接写入 TLS 记录,跳过二次加密。实测在 4 核 VPS 上,Vision 相比普通 VLESS 能省 30%~40% CPU,吞吐提升明显。

回落配置要点:未通过认证的连接(比如主动探测、扫描器)会被 Reality 转发到 fallbacks.dest。生产环境里通常让 Reality 直接回落给 Nginx/Caddy 上的真实站点:

"fallbacks": [
  { "dest": "127.0.0.1:8443", "xver": 1 }
]

Nginx 侧:

server {
    listen 8443 ssl proxy_protocol;
    server_name www.microsoft.com;
    ssl_certificate     /etc/nginx/cert/fullchain.pem;
    ssl_certificate_key /etc/nginx/cert/privkey.pem;
    # 关键:让 Nginx 也接受 PROXY protocol,还原真实客户端 IP
    set_real_ip_from 127.0.0.1;
    real_ip_header proxy_protocol;
    location / { return 200 "ok"; }
}

xver: 1 开启 PROXY protocol v1,把真实客户端 IP 透传给后端。不加这个,Nginx 日志里全是 127.0.0.1,排障时抓瞎。


5. 主动探测与中间人拦截实测验证

5.1 主动探测模拟:JA3/JA4 对比

先看真实 Chrome 访问 www.microsoft.com 的指纹:

curl -svo /dev/null https://tls.peet.ws/api/all \
  --tlsv1.3 --tls-max 1.3 2>&1 | jq '.tls.ja3_hash, .tls.ja4'

自建探测脚本(Python + tls-client)模拟 Reality 客户端,再对比:

import tls_client
s = tls_client.Session(client_identifier="chrome_120")
r = s.get("https://tls.peet.ws/api/all")
print(r.json()["tls"]["ja3_hash"])
print(r.json()["tls"]["ja4"])

实测对比表(同一台机器,同一出口 IP):

流量类型JA3 HashJA4扩展顺序一致性ALPN
真实 Chrome 120cd08e31494f9531f560d64c695473da9t13d1516h2_8daaf6152771_b0da82dd1658基准h2,http/1.1
Reality + chrome 指纹cd08e31494f9531f560d64c695473da9t13d1516h2_8daaf6152771_b0da82dd1658完全一致h2,http/1.1
Reality + firefox 指纹579ccef312d18482fc42e2b822ca2430t13d1516h2_8daaf6152771_...与 Firefox 一致h2,http/1.1
裸 Xray VLESS+TLS3b5074b1b5d032e5620f69f9f700ff0et13d1517h2_...与 Chrome 不符h2

结论:Reality 的 uTLS 指纹伪装在 JA3/JA4 层面与真实浏览器完全一致,这是它相比普通 TLS 代理的核心优势。但注意,JA4 里的 h2 表示 ALPN 协商结果,如果服务端只支持 http/1.1,JA4 会变成 h1,与浏览器不符。

5.2 中间人抓包核验

用 Wireshark 解密 TLS 1.3 需要 SSLKEYLOGFILE。Reality 客户端不导出 keylog,所以只能抓明文侧或使用 tcpdump 看握手结构。

# 抓 ClientHello
sudo tcpdump -i eth0 -nn -s0 -w reality.pcap 'tcp port 443 and (tcp[((tcp[12] & 0xf0) >> 2)] = 0x16)'

Wireshark 打开后过滤 tls.handshake.type == 1,展开 ClientHello,重点看:

  • Extension 顺序:Chrome 是 server_name → extended_master_secret → renegotiation_info → supported_groups → ec_point_formats → session_ticket → ALPN → status_request → signature_algorithms → ...
  • supported_groups:Chrome 是 x25519, secp256r1, secp384r1
  • signature_algorithms:Chrome 包含 ecdsa_secp256r1_sha256, rsa_pss_rsae_sha256, ...

Reality + chrome 指纹的抓包结果与真实 Chrome 逐字节一致。这就是 uTLS 的价值,它不是在协议层”接近”,而是直接复刻了 ClientHello 字节流。

5.3 证书链一致性验证

# 直接访问 Target
openssl s_client -connect www.microsoft.com:443 -servername www.microsoft.com -showcerts </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256

# 访问 Reality 节点(伪装成 Target)
openssl s_client -connect 1.2.3.4:443 -servername www.microsoft.com -showcerts </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256

两条命令输出的 SHA256 指纹必须完全一致。Reality 的做法是:客户端发起 ClientHello 时携带 server_name=www.microsoft.com,服务端收到后实时向真实 Target 发起一次 TLS 握手,把 Target 返回的证书链原样透传给客户端。所以证书不是伪造的,是 Target 的真证书。

验证证书链无自签痕迹:

openssl s_client -connect 1.2.3.4:443 -servername www.microsoft.com -showcerts </dev/null 2>/dev/null | \
  openssl crl2pkcs7 -nocrl -certfile /dev/stdin | \
  openssl pkcs7 -print_certs -noout

输出应显示 subject=CN=www.microsoft.com 且 issuer 为 DigiCert/Microsoft 等真实 CA,绝不能出现 self signed。

5.4 回落行为验证

未认证连接的回落测试:

# 用 curl 直接访问 Reality 节点,不携带任何 Reality 认证
curl -sv https://1.2.3.4/ --resolve www.microsoft.com:443:1.2.3.4 2>&1 | head -30

预期结果:TLS 握手成功,返回的 HTTP 响应与直接访问 www.microsoft.com 完全一致,包括响应头、Server 字段、Set-Cookie、甚至 CDN 的 cf-ray 头。任何差异(比如多了 X-Powered-By、少了 cf-ray)都会成为主动探测的识别特征。

对比验证:

diff <(curl -sI https://www.microsoft.com) <(curl -sI https://1.2.3.4 -k --resolve www.microsoft.com:443:1.2.3.4)

理想情况下 diff 为空。实践中会有 Date 头差异,其他应一致。

5.5 典型错误日志分析

TLS Alert 40(handshake_failure)

2024/01/15 10:23:41 [Warning] failed to handler mux client connection > vless: failed to dial to 1.2.3.4:443: tls: handshake failure

原因:客户端 server_name 不在服务端 serverNames 列表,或 public_key 与 private_key 不匹配。排查:

# 检查公钥是否与私钥配对
xray x25519 -i "私钥base64"
# 输出应等于客户端配置的 public_key

Handshake Timeout

failed to process outbound > dial tcp 1.2.3.4:443: i/o timeout

原因:服务端 443 被墙,或 dest 站点不可达导致 Reality 无法完成向 Target 的握手。排查:

# 从服务端测试 dest 可达性
curl -sv https://www.microsoft.com:443 -o /dev/null
# 检查本地防火墙
ss -tulpn | grep :443

RST Injection(RST 注入)

read: connection reset by peer

原因:中间设备检测到异常流量后注入 RST。排查:

# 用 mtr 看丢包位置
mtr -T -P 443 -c 100 www.microsoft.com
# 用 tcpdump 看 RST 来源
sudo tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0'

如果 RST 来自非服务端 IP,说明是中间设备注入。此时需要更换端口(443→8443)、更换 Target、或启用 CDN 中转。


6. 抗封锁演进与未来风险

6.1 JA4+ 与 ECH 的冲击

JA4+ 相比 JA3 增加了 TLS 扩展顺序、ALPN、签名算法等维度的哈希,且引入了 JA4H(HTTP 指纹)、JA4X(证书指纹)、JA4S(服务器响应指纹)等子集。对 Reality 的冲击在于:

  • JA4S:服务端响应指纹。Reality 服务端在回落时返回的是 Target 的真实响应,JA4S 与 Target 一致,无风险。但如果服务端直接响应(认证成功),JA4S 会暴露 Xray 的 TLS 栈特征。应对:确保认证流量也走 Vision,让 TLS 层行为与 Target 一致。
  • JA4X:证书指纹。Reality 透传 Target 证书,JA4X 一致,无风险。

ECH(Encrypted Client Hello)是更大的变量。ECH 把 SNI 加密进 encrypted_client_hello 扩展,中间设备看不到 SNI。这看似对 Reality 有利(SNI 不再暴露),实则不然:

  • ECH 需要 DNS HTTPS 记录发布 ech 公钥,Reality 的 Target 站点大多未部署 ECH。
  • 若客户端强制 ECH 而服务端不支持,握手会失败回退,回退过程本身是识别特征。
  • 未来若 ECH 普及,Reality 的”SNI 偷取”逻辑需要重构为”ECH 参数偷取”,复杂度上升。

当前应对策略:不主动启用 ECH,保持与 Target 站点一致的 ECH 支持状态。Target 不支持 ECH,客户端也不发 ECH 扩展。

6.2 SNI 偷取的法律与合规边界

这是行业里最不愿公开讨论的部分。技术上的”SNI 偷取”本质是:你的服务器在 TLS 握手时冒充另一个域名的身份。这在多数司法管辖区处于灰色地带:

  • Target 站点授权:未经授权使用他人域名作为 SNI 伪装目标,可能构成商标侵权或不正当竞争。微软、苹果等大厂对此态度暧昧,它们不会主动追责个人用户,但大规模滥用(比如某 IP 段每天数百万次握手)会触发其安全团队。
  • CDN 服务条款:若 Target 是 Cloudflare 后面的站点,你的 Reality 流量会经过 CF 边缘节点。CF 的 ToS 明确禁止”规避服务限制”的行为,滥用可能导致 CF 封禁你的源站 IP。
  • 滥用风险:Reality 节点被用于灰产(爬虫、欺诈、DDoS 跳板)时,Target 站点会收到大量异常流量,你的节点 IP 会被标记为恶意。这是”灰产 IP 污染”的根源。

合规做法:选择明确允许此类用途的 Target(如自建站点),或使用 Cloudflare 的 workers.dev 子域作为 Target(CF 对此相对宽容)。

6.3 与 XHTTP、Hysteria2、TUIC 的组合取舍

协议传输层抗封锁吞吐CPU适用场景
Reality + VisionTCP强(TLS 伪装)高中主力节点,稳定优先
XHTTPHTTP/2 或 HTTP/3中(依赖 CDN)中低CDN 中转,隐藏源站
Hysteria2QUIC/UDP中(UDP 易被 QoS)极高高高丢包链路,游戏加速
TUICQUIC/UDP中高中低延迟场景

实测吞吐对比(4 核 8G VPS,1Gbps 出口,客户端同城):

协议下行 Mbps上行 MbpsRTT ms抖动 ms丢包 %服务端 CPU %
Reality+Vision920880121.20.038
XHTTP (CF)340310458.50.312
Hysteria2950910110.80.062
TUIC890850131.50.155
裸 VLESS+TLS780740142.00.152

取舍逻辑:

  • 稳定优先、抗封锁优先:Reality + Vision,TCP 443 端口最不显眼。
  • 高丢包链路(跨境、移动网络):Hysteria2,QUIC 的拥塞控制对丢包不敏感,但 UDP 容易被运营商 QoS。
  • CDN 中转:XHTTP over HTTP/2,源站 IP 完全隐藏,代价是延迟增加。
  • 组合方案:Reality 作为主入口,Hysteria2 作为备用,客户端根据网络质量自动切换。

6.4 长期运维策略

Target 轮换:每 3~6 个月更换一次 Target,避免长期关联。轮换时注意客户端 server_name 同步更新,否则握手失败。

指纹库更新:uTLS 的指纹模板随浏览器版本更新。Chrome 每 4 周一个大版本,指纹可能变化。运维上要:

# 定期检查 uTLS 指纹库版本
go list -m -u github.com/refraction-networking/utls
# 更新后重新编译客户端

多节点冗余:至少 3 个节点,分布在不同 ASN、不同地理区域。客户端配置 urltest 或 fallback 策略:

{
  "type": "urltest",
  "tag": "auto",
  "outbounds": ["node-hk", "node-jp", "node-sg"],
  "url": "https://www.gstatic.com/generate_204",
  "interval": "3m",
  "tolerance": 50
}

被动流量分析对抗:高级 DPI 会做流量时序分析,Reality 长连接的包间隔、包大小分布与真实 HTTPS 浏览有差异。对抗手段:

  • 启用 Vision 的 padding 选项,填充包大小。
  • 避免长时间单连接大流量,定期重连。
  • 混合真实浏览流量(比如节点同时跑一个真实网站),稀释异常特征。

真假专线识别:市场上大量”IPLC 专线”实为普通 BGP 中转,识别方法:

# 看路由跳数
mtr -T -P 443 -c 100 目标IP
# 真专线通常 3~5 跳,普通中转 10+ 跳
# 看延迟稳定性
# 真专线抖动 < 1ms,普通中转抖动 5~20ms

超售识别:VPS 商家超售 CPU/带宽是常态。识别方法:

# 连续 24 小时监测
while true; do
  echo "$(date) $(curl -s -o /dev/null -w '%{speed_download}' https://speed.cloudflare.com/__down?bytes=100000000)"
  sleep 300
done

若峰值吞吐远低于标称(比如标称 1Gbps 实测 200Mbps),且不同时段波动大,基本是超售。

灰产 IP 污染:节点 IP 若被前租户用于灰产,会被各大平台标记。检测:

# 查询 IP 信誉
curl -s "https://api.abuseipdb.com/api/v2/check?ipAddress=1.2.3.4" -H "Key: YOUR_KEY"
# 或访问 https://scamalytics.com/ip/1.2.3.4

信誉分低于 50 的 IP 直接弃用,否则你的正常流量也会被 Cloudflare 挑战、被 Google 验证码拦截。


Reality 的技术本质是把”伪装”做到协议层字节级一致,但它的天花板由 Target 站点、中间设备能力和法律边界共同决定。工程上能做的,是持续测量、持续轮换、持续对抗,没有一劳永逸的配置,只有不断演进的博弈。

常见问题解答 (FAQ)

Reality 为什么不需要自有域名和自签名证书?
Reality 的核心设计是偷取真实高信誉站点的 SNI。客户端在 ClientHello 中填入 Target 站点的 SNI,服务端在 TLS 1.3 握手时借用该 Target 的真实证书链完成认证。由于服务端不持有域名、不申请证书,因此不存在自签证书或 Let's Encrypt 证书可被主动探测识别的问题。认证通过 X25519 临时密钥与 Target 公钥派生完成,未通过认证的连接被透明转发至真实 Target,使探测者无法区分代理与真实站点。
JA3 和 JA4 指纹的核心区别是什么?为什么 JA4 更难伪造?
JA3 仅对 cipher_suites、extensions、elliptic_curves、ec_point_formats 做 MD5 拼接,字段顺序固定且不包含 ALPN、SNI 与签名算法,容易被 uTLS 模板碰撞。JA4 采用三段式设计:JA4_a 编码协议版本、SNI 存在性、ALPN 与扩展数量;JA4_b 编码 cipher_suites;JA4_c 编码 extensions 与签名算法。JA4 引入了更多上下文信息,且对扩展顺序敏感,使简单的 uTLS 模板难以完全匹配,因此更难伪造。
挑选 Target SNI 时必须满足哪些硬性条件?
Target 站点必须满足:支持 TLS 1.3 且优先协商 X25519 密钥交换;启用 OCSP Stapling 以便服务端在握手时携带有效 OCSP 响应;ALPN 必须包含 h2 与 http/1.1;证书链完整且由公共 CA 签发;站点本身未被墙且 IP 稳定。此外,Target 的 JA3/JA4 指纹应与客户端 uTLS 模板一致,否则主动探测可通过指纹不匹配识别代理。建议使用 openssl s_client 与 tls.peet.ws 批量校验。
Reality 服务端的回落行为是如何实现的?
Reality 服务端在 TLS 1.3 握手阶段解析 ClientHello,若客户端携带的认证信息(基于 X25519 派生)校验失败,服务端不会中断连接,而是将整个 TLS 会话透明转发至配置的 dest(Target 站点)。转发过程中,服务端充当 TCP 代理,保持与 Target 的完整 TLS 握手,使探测者看到的证书、响应内容与直接访问 Target 完全一致。只有认证通过的连接才会被服务端接管并进入代理模式。
如何通过抓包验证 Reality 流量与真实合法网站流量一致?
使用 Wireshark 配合 TLS 1.3 密钥日志解密握手,重点比对 ClientHello 的扩展顺序、cipher_suites、ALPN 与 SNI 是否与真实浏览器一致。同时使用 tls.peet.ws 或 ja3er.com 对比 Reality 流量与直接访问 Target 的 JA3/JA4 指纹。证书链方面,确认 Reality 返回的证书与 Target 站点证书的 issuer、subject、SAN 完全一致。回落验证时,用未认证客户端连接 Reality 端口,响应内容应与直接访问 Target 完全一致。
Reality 的 shortId 和 privateKey/publicKey 如何正确生成与配置?
使用 xray x25519 命令生成一对 X25519 密钥,privateKey 配置在服务端 realitySettings 中,publicKey 配置在客户端。shortId 是 0 到 16 位十六进制字符串,服务端可配置多个 shortId 供不同客户端使用,客户端需与服务端其中一个匹配。shortId 并非密钥,仅用于区分用户与防止重放。建议每个用户分配独立 shortId,定期轮换,避免多用户共用导致流量特征关联。
Reality 能否抵御中间人主动探测?其边界在哪里?
Reality 能抵御大多数主动探测,因为未认证连接被透明转发至真实 Target,探测者无法通过证书异常或响应差异识别代理。但其边界在于:若探测者掌握 Target 站点的真实 IP 与 Reality 服务端 IP 不一致,可通过 IP 比对发现异常;若 Target 站点被墙或证书轮换,Reality 服务端会失效;若 JA4+ 或 ECH 普及,指纹伪装难度将显著上升。因此 Reality 并非绝对安全,需配合 Target 轮换与指纹库更新。
Reality 与 Trojan、WebSocket+TLS 相比有哪些本质优势?
Trojan 与 WebSocket+TLS 均依赖自有域名和自签或 Let's Encrypt 证书,证书透明度日志会暴露域名,主动探测可通过证书异常识别代理。Reality 不持有域名、不申请证书,借用 Target 真实证书链,从根本上消除了证书层面的识别特征。此外,Reality 的回落行为使未认证连接与真实站点流量完全一致,而 Trojan 的回落通常需要额外配置且易出现响应差异。Reality 在架构上更接近真实 TLS 流量,抗封锁能力更强。
建议下一步阅读 深入解决与决策

经核验的稳定机场与专线推荐

查看基于真实专线网络与独立测速评测的推荐清单