Skip to content

第25章 从完整配置还原浏览器到网站的通信 ​

25.1 先固定一组能够解释清楚的配置 ​

本章把前面各层组合成一个具体对象:浏览器经本机SOCKS5入口连接网站,本地Xray用RAW承载VLESS Encryption,VLESS请求启用Vision,远端Xray再连接网站。实现仍固定为Xray-core提交0888beb38a9ba45440557175e71ce21cf8ba3b13。本章的“完整配置”指两端结构齐全、字段及组合经固定源码静态核对;没有在本书写作环境中编译该提交并完成两端互通,不能把配置展示视为网络测试报告。

选择RAW且外层security为none,是为了直接观察Encryption与Vision的关系。none在这里只表示不再额外包装TLS/REALITY,VLESS Encryption仍然启用。固定出站安全检查接受非none的VLESS Encryption;Vision的处理也明确接受encryption.CommonConn。不能把早期版本的“Vision只能直接配TLS/REALITY”结论或代码中的旧错误提示,套到这个提交所有分支上。

以下所有身份与地址都是公开教学材料。UUID沿用第19章,203.0.113.10属于文档地址段,不指向一台本书提供的代理服务器。静态X25519私钥和公钥取自RFC 7748第6.1节Bob的公开测试向量,经无填充Base64URL表示。它们不是作者或读者的实际密钥;任何人都知道该私钥,故这份配置不具备生产秘密性,也不能作为可直接部署的安全凭据。

客户端完整JSON如下:

json
{
  "log": { "loglevel": "warning" },
  "inbounds": [
    {
      "tag": "browser-socks",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": { "auth": "noauth", "udp": false }
    }
  ],
  "outbounds": [
    {
      "tag": "to-proxy",
      "protocol": "vless",
      "settings": {
        "address": "203.0.113.10",
        "port": 443,
        "id": "00112233-4455-4677-8899-aabbccddeeff",
        "flow": "xtls-rprx-vision",
        "encryption": "mlkem768x25519plus.native.1rtt.3p7bfXt9wbTTW2HC7OQ1Nz-DQ8hbeGdNrfx-FG-IK08"
      },
      "streamSettings": { "network": "raw", "security": "none" }
    }
  ]
}

服务端完整JSON如下:

json
{
  "log": { "loglevel": "warning" },
  "inbounds": [
    {
      "tag": "from-client",
      "listen": "0.0.0.0",
      "port": 443,
      "protocol": "vless",
      "settings": {
        "clients": [
          {
            "id": "00112233-4455-4677-8899-aabbccddeeff",
            "flow": "xtls-rprx-vision"
          }
        ],
        "decryption": "mlkem768x25519plus.native.600s.XasIfmJKikt54X-Lg4AO5m87sSkmGLb9HC-LJ_-I4Os"
      },
      "streamSettings": { "network": "raw", "security": "none" }
    }
  ],
  "outbounds": [ { "tag": "to-site", "protocol": "freedom", "settings": {} } ]
}

客户端采用该提交支持的简化VLESS出站结构,address、port、id、flow、encryption直接放settings。它不是把旧vnext结构的一半与新结构拼在一起。服务端的decryption位于入站settings;不能放入clients里的单个用户对象,后者出现encryption反而会被构建器拒绝。

两端native必须一致。客户端1rtt表示本实例不使用缓存恢复来发送早期代理数据,服务端600s是它提供的恢复时间参数,两者没有逐字匹配要求。服务端能提供票据,不会强迫客户端必须使用它。静态密钥部分使用X25519,之后完整握手仍包含第22章的临时ML-KEM-768与X25519混合交换;协议名中的mlkem不意味着末尾每个静态公钥都必须长1184字节。

本章固定请求网站为example.com:443,并假定浏览器把域名交给SOCKS入口。没有启用嗅探、TUN、代理链、额外Mux或UDP,因此目标信息来自SOCKS请求,而不是由某个未展示的规则猜出。唯一客户端出站负责发往代理服务器,唯一服务端出站负责访问目标。文档IP避免为了连接代理端点而另行解析其域名;目标域名的解析仍发生在远端直连路径上。

配置要素运行时首先影响的对象不由它决定的事项
127.0.0.1:10808本地SOCKS监听Socket网站TLS证书是否可信
203.0.113.10:443客户端连接的代理端点VLESS所请求的网站地址
idVLESS用户标识及查找静态密钥和临时密钥
encryption/decryptionEncryption握手及记录包装网站TLS所选组与证书
flowVLESS Addon及Vision处理保证每种数据都能进入Direct
network: raw直接流承载使用Linux splice的全部条件

本节依照infra/conf/vless.go、传输构建器、出站安全检查以及VLESS出站静态核对。公开密钥向量见RFC 7748第6.1节。

25.2 浏览器交付的是目标与字节,不是代理服务器的地址 ​

假定浏览器要访问https://example.com/,并在这个案例中选择通过本地SOCKS5传递域名。它先与127.0.0.1:10808建立一条本机TCP连接。这个Socket的远端是本机Xray,不是example.com;但随后发出的SOCKS命令描述的是example.com:443,不是203.0.113.10:443。

方法协商可以是以下确定字节:

text
浏览器 → 本地Xray:05 01 00
本地Xray → 浏览器:05 00

前者表示SOCKS5、只提供一种方法、该方法为无认证;后者表示接受无认证。这份配置的入口只监听回环地址,noauth也只是本地入口的认证策略,与远端VLESS用户认证和Encryption服务器密钥无关。

目标请求为18字节:

text
05 01 00 03 0b 65 78 61 6d 70 6c 65 2e 63 6f 6d 01 bb

其中03在SOCKS5表示域名;0b之后是11字节example.com,最后01 bb为443。注意第19章的VLESS域名类型是02,且VLESS把端口放在地址类型之前。Xray会先把SOCKS请求解析为内部目标对象,再重新编码VLESS;它没有直接把这18字节粘到远端连接上。

固定Xray实现还有一个与观察结果有关的细节:SOCKS ServerSession.Handshake在完成合法命令解析后就写出statusSuccess,外层server.processTCP随后才执行DispatchLink。也就是说,本实现本地SOCKS的成功应答可以早于远端代理连接及目标连接建立。不能只凭浏览器收到了05 00开头的CONNECT应答,就推断远端已与网站完成TCP握手。这是具体代理实现中的应答时机,应与第6章介绍的SOCKS回复字段语义及最终转发结果分别理解。

进入隧道阶段后,浏览器在这条本机TCP连接中发送网站TLS ClientHello。SOCKS头已被消费,后续字节交给本地Xray的转发链路。浏览器仍保有TLS私有状态:临时私钥、握手摘要、证书验证结果和应用流量密钥由浏览器维护;Xray通常只把这些TLS记录当成待转发的字节,并由Vision观察有限的可识别结构。

这里不要求浏览器一定把完整ClientHello放在一次Write或一个TCP段中。多个读取缓冲可以对应同一个TLS握手消息,反之一次读取也可能包含多个记录。正确的转发层保留字节顺序;Vision是否及时识别,另受第23章列出的有限检测窗口与分片条件影响。

配置没有指定所有浏览器功能必须经过SOCKS。这个案例只追踪已经选择该入口的TCP连接;浏览器独立DNS、系统解析、已有直连连接和其他应用流量不由这一个Socket设置自动接管。特别是本章udp为false,不能把网站可能使用的HTTP/3/QUIC流量默认为已通过这条TCP路径。下文分析的是网站TLS上的TCP通信。

SOCKS握手及应答时机见固定protocol.go和server.go,格式基线见RFC 1928。

25.3 三条TCP连接与两种“目标” ​

SOCKS入口产生的内部目标是tcp:example.com:443。本地分发器把它交给唯一的to-proxy出站;该出站另有服务器端点tcp:203.0.113.10:443。前者稍后写入VLESS请求,后者现在用于拨号。代理端点与最终目标虽然都使用443端口,却是不同角色。

配置没有testpre,因此不会进入该提交的预连接池分支。VLESS出站调用dialer.Dial连接代理端点;RAW拨号路径调用系统拨号器取得TCP连接。本例既没有TLSSettings也没有REALITYSettings,故传输拨号器不会在此执行那两种安全握手。它返回后,VLESS出站才调用Encryption.Handshake包装该连接。

TCP建连需要SYN、SYN-ACK、ACK等内核状态转换。应用得到可读写连接后,发出的字节仍会受拥塞控制、发送缓冲和对端接收状态约束。不能把“Dial返回”理解为网站已连接,也不能把连接到443解释为内核自动启用TLS。端口本身不会为数据添加任何密码保护。

最终成功的本章路径有三条分别终结的TCP连接:

text
浏览器 ── TCP A:127.0.0.1:10808 ── 本地Xray
本地Xray ── TCP B:203.0.113.10:443 ── 远端Xray
远端Xray ── TCP C:目标解析地址:443 ── 网站

A、B、C有独立序号、ACK、窗口、重传和关闭状态。A上的一次ACK只说明本机TCP接收了对应字节,不代表C已把它送到网站。Xray从一个连接读取、在另一个连接写入,建立的是应用字节的转发关系,不是把TCP头沿三段原封不动搬过去。

TCP C尚未在本节建立。远端必须先完成Encryption并解析VLESS,才知道这条会话请求哪个网站。没有VLESS目标或其他等价路由输入,单靠TCP B的目的203.0.113.10:443无法恢复example.com。若网络中另有普通NAT,它只改写相关IP/端口与校验,不负责解释这个加密后的代理目标。

浏览器的网站TLS却跨越A、B、C转发后仍与网站建立同一份端到端安全会话。其TLS记录可以经过不同TCP分段,也可以在B上先被Vision与Encryption再封装。连接分段不等于TLS终结:只有掌握对应TLS秘密、参与握手并通过认证的端点,才能按该TLS会话解释网站明文。

固定的拨号顺序见VLESS outbound.Process,RAW拨号与额外安全包装分支见tcp/dialer.go。

25.4 先完成Encryption,VLESS头才进入记录层 ​

本章配置的静态材料是一段32字节X25519公钥,native表示不额外混淆静态公钥材料或记录头,1rtt表示走完整临时交换。客户端先生成新的16字节IV及一对临时X25519密钥,把32字节临时公钥送给服务器;服务器结合自己的静态私钥计算与客户端相同的nfsKey。这里的“临时”指这次静态认证交换使用的新客户端份额,还不是随后形成PFS混合秘密的那一对密钥。

接下来,客户端另外生成临时ML-KEM-768密钥对及临时X25519密钥对。它把1184字节ML-KEM公钥和32字节X25519公钥组成1216字节公开材料,并使用从nfsKey及本次上下文派生的AEAD保护。握手长度字段也经AEAD保护。因此不能在TCP中搜索一个明文的VLESS版本00,就认定找到了代理请求起点;此时VLESS尚未发送。

本例单静态X25519材料的客户端完整握手发送长度为:

text
IV 16 + 静态交换客户端公钥 32
+ 加密长度 18
+ 加密临时公开材料 (1216 + 16)
+ 客户端握手Padding Pc
= 1298 + Pc 字节

Pc是第22章定义的整个Padding结构,不只是填充正文。配置没有指定自定义Padding,故由固定实现默认策略抽样,不能给本次会话编造一个确定值。这些字节可以分多次Write、多个TCP段发送;等式统计协议字节,不统计IP/TCP头或重传。

服务器处理临时ML-KEM公钥,生成1088字节封装密文及32字节共享秘密;另生成自己的临时X25519公钥,与客户端对应份额得到第二个32字节共享秘密。两端形成:

text
pfsKey = ML-KEM共享秘密32 || 临时X25519共享秘密32
UnitedKey = pfsKey64 || nfsKey32

服务器返回的1120字节临时公开材料由nfsKey相关AEAD保护,密文含Tag共1136字节。此后使用新的方向记录密钥保护16字节票据及服务器Padding:总回复为1168+Ps字节。上行与下行的密钥派生上下文不同,分别绑定本次客户端或服务端公开交换材料;同一个UnitedKey不意味着双向使用同一份AEAD状态。

第22章已逐字节解释BLAKE3 derive_key、Nonce预增、特殊回复Nonce、方向上下文及票据。落实到此配置,重要的是顺序:服务器能处理合法Encryption连接后,才把解密流交给VLESS;客户端完整握手得到CommonConn后,才编码和发送VLESS头。服务器握手回复不是VLESS用户认证成功,也不包含网站example.com的证书。

1rtt客户端没有把这次票据保存为后续代理连接的0-RTT恢复状态。因此之后新建的同类连接仍需完整交换,不能因为服务端600s启用了会话表就删去临时混合交换。服务端提供的恢复参数与客户端是否采用它是两件事。

在真正保密的部署中,固定服务器公钥提供的认证含义以其分发可信且对应私钥保密为前提。本章刻意使用公开RFC私钥,只能演示字段、长度和路径,不能提供该前提。另须保留第22章针对固定客户端的实现边界:首次完整服务器公开材料Open的返回错误未直接检查,后续解析与票据认证另有检查;不能把本节顺序图理解成每个错误都会在最早可能的位置被拒绝。

更重要的是,UnitedKey曾经建立混合保护,不代表网站所有后续字节永远套着它。Vision可能在满足条件后绕过CommonConn记录;发生在哪个方向、保留哪层承载,由后续状态决定。对最终网站数据的PFS与后量子结论,必须结合该切换及网站自身TLS,而不能只读Encryption字符串。

握手与材料拼接见固定client.go、server.go和common.go。

25.5 52字节请求头与第一块网站TLS数据 ​

Encryption得到可用CommonConn后,本地Xray把内部目标编码为VLESS。flow设置使Addon包含Protobuf字段Flow,其值为16字节ASCII字符串xtls-rprx-vision。字段键0a、长度10和正文组成18字节,外面的Addon长度因而是12(十六进制)。

本章请求头共52字节,全部列出如下;此处展示Encryption解密后的逻辑字节,不是直接可见的TCP B明文:

text
00
00 11 22 33 44 55 46 77 88 99 aa bb cc dd ee ff
12
0a 10 78 74 6c 73 2d 72 70 72 78 2d 76 69 73 69 6f 6e
01 01 bb 02 0b 65 78 61 6d 70 6c 65 2e 63 6f 6d

第一行为版本,第二行为原始UUID,第三行为Addon长度,第四行为Addon,最后一行为TCP命令、端口、域名类型、域名长度和域名。命令在偏移36,端口在37—38,域名起于41。与25.2节SOCKS请求相比,身份和Flow是本地出站新增的,地址类型与字段顺序也已转换。

请求头后面不是直接把浏览器所有字节永久逐字节附上。Vision先观察网站TLS数据,并在尚未切换的阶段形成Padding块。每个方向第一次标记块有16字节UUID前缀,随后是命令1字节、内容长度2字节、Padding长度2字节,再接内容和Padding。这个UUID用于识别Vision初始块,与请求头里用于VLESS用户查找的UUID处于不同结构位置。

可以选取一个明确的长度构造:浏览器交来一段201字节的TLS记录,Vision此次选择Padding为799字节。这里不伪造201字节记录的完整内容,只把它作为长度已知的待转发数据;799是一次允许的构造选择,不是每条连接固定值。第一块长度为16+5+201+799=1021字节。如果当前仍继续Padding,命令为00,两个长度字段分别为00 c9、03 1f。

若52字节VLESS头与这1021字节恰好合在同一次CommonConn.Write中,且不超过记录分块阈值,Encryption的输入为1073字节,记录增加5字节头和16字节Tag,输出1094字节。若两者分成两次CommonConn.Write,则通常对应两个记录,合计1115字节。缓冲和调用边界影响记录数;TCP再次分段并不额外生成Encryption记录。

默认出站先把VLESS头写进BufferedWriter,再尝试取得首批应用数据。对支持超时读取的内部Reader,源码最多等待一次500 ms;成功取得则交给Vision写入,超时分支可插入空内容的Vision Padding,随后取消头部缓冲。不能把这500 ms读成每个请求必然增加500 ms:浏览器数据已经可读时会提前继续;不同Reader路径也可能单独发送请求头。

出站随后继续复制浏览器字节。是否以及何时发出Vision命令02,取决于后续观察到的网站ServerHello、所选TLS版本/密码套件、记录完整性与当前状态。配置flow并没有让第一块就无条件绕过Encryption。另一端还要先收到并解析这些块,才能以相同字节顺序恢复浏览器发出的网站TLS内容。

字段编码见固定encoding.go与addons.go,首批读取和Flush见outbound.go,Vision块见proxy.go。

25.6 远端恢复字节、解析目标,再建立网站连接 ​

服务器accept取得TCP B后,入站首先执行配置的Encryption握手。握手失败会在读取VLESS之前退出;成功后读取的是CommonConn提供的解密字节。由于本章没有fallbacks,不存在把短首读直接改走VLESS回退的配置。请求头即使跨读取边界,解析器仍可继续收集所需字段。

解析过程按25.5节顺序取得版本、用户标识、Addon、命令及目标。固定实现用户查找会对UUID索引6、7做第18章说明的归一化;原始userSentID仍保存,用于Vision TrafficState等路径。因此“找到账号”与“原始16字节被每一步逐字节当作同一认证值比较”不是同一结论。本章两端使用相同公开UUID,避免把该特殊映射引入额外变化。

服务端检查账号允许xtls-rprx-vision,客户端请求也携带同样Flow,再根据CommonConn准备Vision读写状态。VisionReader去除初始UUID、块头与Padding,只把块内容交给下游。去掉这些包装后,应还原浏览器原先提供的网站TLS字节;不会把VLESS头或Encryption票据发送给example.com。

入站把example.com:443交给唯一freedom出站。这里需要补上一个容易被“直连”二字掩盖的版本细节:固定提交的freedom会根据入站类型应用默认final规则,VLESS属于默认阻止私有目标的类别。对于域名,它可在拨号前解析检查,并在连接建立后检查实际远端地址;命中阻止规则会进入相应黑洞处理。因此即使没有显式routing,freedom也不能概括为无条件允许任意内网地址。

本章以正常解析到公开地址的example.com为条件。配置没有自定义DNS或sockopt域名策略,默认final检查路径会使用系统解析器取得地址;随后拨号器对域名的处理也可能涉及解析及选址。不能把它简化成“全过程必定只发一条DNS查询”,查询次数还受缓存、地址族和实际拨号路径影响。可以确定的是,这些目标解析发生在远端主机侧,不是把查询包自动倒送给浏览器。

系统解析器使用哪个递归服务器、是否加密查询、是否命中本地缓存,由远端系统和相关运行环境决定。本章配置没有声明DoH,故不能凭“已经走加密代理”就声称远端DNS也必然加密。代理链保护的边界没有自动延伸到系统解析器与其上游之间。

取得允许的目标地址后,远端通过TCP C连接网站。目标可以观察到远端出口地址和所连接端口,通常看不到TCP A的本机回环地址。本章没有PROXY protocol配置,不会主动把浏览器源地址作为这种专用前置头发给网站。网站仍可能通过账户、Cookie或应用标识识别用户;出口IP变化不等于应用身份消失。

网站TCP建立后,转发任务把Vision还原的ClientHello等字节写入C。网站TLS实现解析它们,选择版本、组和密码套件,返回ServerHello及后续握手。远端Xray不替浏览器验证网站证书;浏览器收到返回字节后,才依据自己的信任库和目标名称进行网站认证。

与此同时,VLESS入站已把默认响应头写进本地BufferedWriter,并设置下一次写出时刷新。这个头的生成发生在DispatchLink之前,实际发送与后续返回数据和缓冲行为相关。00 00并没有“网站TCP成功”或“DNS成功”的状态字段,不能用它取代目标连接的真实结果。

入口解密、用户/Flow检查及响应缓冲见固定inbound.go;默认final规则、解析前后检查和转发见freedom.go。这些结论限于所列提交,并非所有历史freedom版本共有的默认行为。

25.7 返回路径:先拆响应,再按方向切换 ​

网站首先返回明文ServerHello所在的TLS记录,后续握手消息则按网站TLS会话的规则加密。远端Xray从TCP C读取这些字节,由VisionWriter观察并包装;在尚未切换的阶段,写入CommonConn的仍是VLESS响应头与Vision块,CommonConn再生成Encryption记录发送到TCP B。

客户端不是把第一个解密字节立即交给浏览器。它先按请求关联状态解析VLESS响应头,消费版本与Addon长度及相应内容,再通过VisionReader去掉Vision块头、初始UUID和Padding。最后交给TCP A的,才是网站返回的原始TLS记录。假如这些包装头泄漏到浏览器,浏览器会把错误字节当作TLS内容,握手无法按预期继续。

两个方向各有自己的Vision块解析和Direct切换状态。能够观察到网站ServerHello,不代表两个方向同时在同一时刻切换。过滤器还要在有限缓冲中识别TLS1.3选择及允许的密码套件,并满足后续记录等条件。第23章解释过,它不是完整TLS状态机,不代替浏览器校验Certificate、CertificateVerify或Finished。

某方向决定发出命令02时,当前带02的Vision块仍通过原先的Writer发送;只有其内容和Padding完全交付后,后续写入才改走Direct路径。接收端也必须先消费完02块,排空相关解密缓冲及已读取的原始缓冲,再替换Reader。两端若把切换点理解为“看到命令字节立刻跳到裸Socket”,就会丢掉当前块余下内容,或把残留密文误当成网站记录。

对于25.1节这组RAW、无额外TLS/REALITY、native Encryption的组合,Direct路径可以绕过CommonConn的AEAD,继续在TCP B传递网站自身TLS记录。它不会拆掉网站TLS,也不会把HTTP明文直接交给网络。Padding阶段的外层混合记录保护已改变;后续数据的密码性质应由仍存在的网站TLS判定。

若配置改成random Encryption,绕过CommonConn后仍可能保留XorConn对五字节记录头的变换,不能照搬native的裸记录描述。若下层改成XHTTP或另加TLS/REALITY,Direct应保留相应承载或安全包装;它不能任意穿透到TCP。固定源码专门设置CanSpliceCopy=3等条件阻止不适合的进一步穿透,这与所有Direct都被禁用并非一回事。

本章配置具备某些splice候选前提,却仍不能保证真正使用Linux splice。平台、底层连接类型、各级就绪状态、复制方向及开关都要满足;相关路径还可能回退为普通读取和写入。第23章对这些判断已经按源码展开,本章没有新增运行时追踪证据,因此不把“可能减少拷贝”写成已测得的吞吐提升。

浏览器最终取得网站TLS握手字节,验证名称example.com对应的证书链、签名与Finished,派生网站应用流量密钥,再发送HTTP请求。远端代理知道目标域名及转发规模,正常情况下并不因此获得这些网站应用密钥。若网站TLS证书验证失败,失败由浏览器判断;即使代理TCP和Encryption一直正常,页面仍不能作为已认证网站内容交付。

网站关闭TLS时,close_notify属于网站自己的安全会话,仍应随字节转发。TCP FIN属于各段运输连接的状态,可能由各端转发任务分别触发。Encryption本身没有TLS式认证关闭通知,故不能从B上读到EOF就单独证明网站应用内容完整;是否完整还要看网站TLS和HTTP的结束条件。

返回处理见固定outbound.go,切换、缓冲排空与层次保留见proxy.go,Encryption记录与关闭边界见common.go。

25.8 HTTP与HTTPS:同一代理路径,不同明文终点 ​

把目标从https://example.com/改成普通HTTP,代理传输仍可能使用Encryption,但安全边界会改变。浏览器发送的可能直接是HTTP请求行与字段,而非网站TLS ClientHello。Vision通常不会据此进入网站TLS Direct路径;停止Padding与绕过外层加密不是同一件事。普通数据仍需经所保留的Encryption记录传递到远端。

远端终结Encryption后,可以读到普通HTTP的方法、路径、Header和Body,再把它们通过TCP C发送给网站。若C没有网站TLS,这段链路的被动观察者也可能读到它们。客户端到代理的一层加密没有延伸成客户端到网站的端到端HTTP保密。

HTTPS则由浏览器与网站维护另一套密钥。代理能解析它请求的example.com:443,也能看到转发的数据长度和时序,但正常转发不赋予代理读取HTTP路径、Cookie和Body的能力。TLS1.3中服务器证书等后续握手内容本身也在网站握手密钥下加密,不能把历史TLS1.2的明文证书可见性直接套过来。

观察位置普通HTTP经本章代理HTTPS经本章代理
浏览器内部完整HTTP明文完整HTTP明文及网站TLS秘密
本地代理解开入口后目标及HTTP明文目标及网站TLS字节
TCP B链路的被动观察者未绕过Encryption时主要见其握手、记录与规模切换前见外层记录,native Direct后可见网站TLS记录与相关元数据
远端代理目标及HTTP明文目标及网站TLS字节
TCP C链路的被动观察者HTTP明文及目标连接元数据网站TLS字节及目标连接元数据
网站发给它的HTTP明文由网站TLS解密后的HTTP明文

表中的密码保护讨论以实际保密密钥部署为前提;25.1节公开测试私钥本身没有这样的保密性。即使不考虑密钥问题,长度与时序也一直没有被完全隐藏。Padding可以扰动某些长度分布,无法证明所有网站或用户行为都不可区分。

域名还有两条不同暴露路径。第一条是代理请求里的目标,本例远端必然知道它。第二条是网站ClientHello里的名称信息。没有ECH保护的网站ClientHello通常包含可识别SNI;若native Direct阶段前的ClientHello仍被Encryption保护,TCP B观察者在该阶段不一定直接看到它,但远端及TCP C路径仍有相应可见性。不能把“浏览器支持ECH”简化成代理就不知道目标域名,因为SOCKS/VLESS仍可能明示域名。

本地DNS泄漏同样不能只靠目标域名由代理解析来排除。若浏览器在发SOCKS请求前已独立做系统查询,查询已经发生;之后再把域名交给远端不会撤销它。反之,本例指定的域名请求确实允许目标解析留在远端,不需要浏览器为了这个CONNECT先得到目标IP。分析隐私必须追踪实际发起解析的组件及其出口。

再看后量子保护。完整Encryption混合交换保护其记录阶段;若Vision在native RAW组合上绕过它,后续网站应用数据只保留网站TLS等仍存在层的保护。Vision并未完整验证网站采用何种密钥交换组。网站若只用传统X25519,不能因为代理配置名包含mlkem,就称后续所有网站应用记录具备混合密钥交换的抗量子性质。

在代理链中,增加一跳会增加一个可以观察其终结层的参与者,也可能分散“知道用户来源”和“知道最终目标”的信息。能否达到这种分离,取决于各跳到底终结哪些安全层以及是否串通;本章两端配置本身不提供匿名性证明。密码保护、出口替换和减少可关联信息是三个不同目标。

普通HTTP与网站TLS边界以第7、9、16章所述协议为基础;本组合Direct后的层次以固定UnwrapCommonConn及Vision读写为证据。这里没有假定对网站进行了TLS中间人终结,也没有从Padding推导不可识别性。

25.9 把时序与封装变化放在同一张图上 ​

下面按一个首次完整交换、网站采用TLS1.3且Vision最终满足Direct条件的连接画图。不含DNS缓存命中以外的假定优化,不含Encryption恢复、TLS早期数据或TCP Fast Open;实际读取与写出仍可交错。图中的网站证书消息是在网站TLS握手保护内传递,不表示它在TCP C上以明文出现。

正在绘制图表…

查看图表原文
sequenceDiagram
    participant B as 浏览器
    participant L as 本地Xray
    participant P as 远端Xray
    participant D as 远端解析系统
    participant W as 网站
    B->>L: TCP A及SOCKS方法、example.com:443
    L-->>B: 本地SOCKS成功应答
    L->>P: 建立TCP B
    L->>P: Encryption静态交换及加密临时公开材料
    P-->>L: Encryption临时回复、票据及Padding
    B->>L: 网站ClientHello(可提前进入本地缓冲)
    L->>P: Encryption记录:VLESS头与Vision封装的数据
    P->>P: 验证用户和Flow,解封Vision
    P->>D: 解析及检查目标地址
    D-->>P: 地址结果(示意,可能来自缓存)
    P->>W: 建立TCP C,转发网站ClientHello
    W-->>P: 网站ServerHello与受保护握手消息
    P-->>L: VLESS响应及Vision封装的网站返回字节
    L-->>B: 原样还原的网站TLS字节
    B->>B: 验证网站名称、证书、签名与Finished
    B->>L: 网站Finished及应用记录
    Note over L,P: 两方向各自按条件发送并消费Vision 02块
    L->>P: 切换后的网站TLS记录(native RAW条件)
    P->>W: 转发网站TLS记录
    W-->>P: 网站应用记录
    P-->>L: 对应方向切换后的记录
    L-->>B: 网站TLS记录

图把浏览器ClientHello放在Encryption回复之后,是为了便于阅读;本地SOCKS已应答时,浏览器也可以更早把它写进TCP A。它先留在本机缓冲,直到出站准备好再前进。这种重叠不代表它跳过Encryption,也不保证所有缓冲容量与调度都相同。

同一网站记录在不同阶段外面包着不同结构:

text
浏览器准备:
网站TLS记录[网站握手或加密HTTP]

TCP B的初期代理数据:
Encryption记录[VLESS请求头 || Vision块[网站TLS记录 + Padding]]
                  ↑只在流起始处出现       ↑块另有长度和命令

TCP C:
网站TLS记录[网站握手或加密HTTP]

TCP B的native RAW Direct阶段:
网站TLS记录[加密HTTP]

第二行只示意可能合并在一个记录中的首批数据,并非要求VLESS头在每条Encryption记录重复,也不要求一个Vision块必须落在单条Encryption记录或TCP段内。下行起始处对应VLESS响应头,而非再发请求头。Direct后的最后一行只适用于本章指定的包装条件,不能作为所有Vision组合的统一封装图。

用时延模型可以看清依赖。忽略本地回环时间,令客户端到远端的RTT为r,远端到网站的RTT为s,远端解析与必要检查耗时为d;假定各方向对称、无丢包、TCP首次建连,网站ClientHello已在本地就绪。客户端完成TCP B约需r,完整Encryption逻辑交换再需约r,首批VLESS及网站ClientHello到达远端再需约r/2。目标解析和TCP C建立随后约需d+s,网站TLS1.3的首个服务器握手批次再约需s到达远端,返回本地另约r/2。

因此,在这组理想化串行条件下,浏览器收到首个网站服务器握手批次的关键路径约为3r+2s+d。它不包含该批次在实际带宽上发送所需时间、Padding间隔、排队、500ms首读超时分支和应用调度,也不代表页面首字节时间。浏览器仍要验证服务器握手、发送自己的Finished与HTTP请求,网站再产生响应。

如果复用的是已有网站连接,或代理链增加一跳,模型就必须重画;不能在这个表达式上任意减去一个“TLS RTT”而不说明删掉了哪个握手。第22章的恢复可以改变Encryption依赖,但保留缓存状态、去重和PFS边界;第24章HTTP承载还会引入自己的请求组织和中间节点等待。公式的用途是定位必须等待的事件,不是给协议贴固定毫秒数。

时序依赖以固定VLESS出站、入站与freedom转发为依据;数值模型是明确假设下的因果推导,没有执行延迟基准测试。

25.10 从失败位置解释浏览器看到的结果 ​

假设浏览器已经取得本地SOCKS成功应答,随后却一直没有收到网站ServerHello。根据25.2节的实现顺序,这并不矛盾:成功应答可能早于TCP B拨号。单靠浏览器的“安全连接失败”或超时提示,无法判定是远端端口不可达、Encryption不匹配、VLESS拒绝、DNS失败还是网站没有返回。

先考虑代理端点不可达。TCP B可能收不到SYN-ACK,也可能收到拒绝连接的RST;此时Encryption尚未开始。固定VLESS出站对初始拨号使用重试退避,但这只是特定阶段的重试,不等于在浏览器已经发送任意应用数据后,都能无损重放到新的连接。不同浏览器会把本地连接关闭或等待超时映射成不同提示,不能为所有软件编造统一错误文本。

若TCP B成功,而客户端静态公钥与服务端私钥不对应,两端不能建立预期的nfsKey保护关系。握手可能在认证解密、公开材料解析或后续检查处失败,具体最早位置依第22章实现路径而异。此时服务器尚未得到一个通过全部检查的VLESS目标;不能从端口能连通就推断账号问题,也不能把所有Encryption错误统称为TLS证书错误,因为本配置根本没有外层TLS证书握手。

若Encryption成功,但UUID不能映射到授权用户,VLESS入站会拒绝请求。账号存在而Flow不一致,同样可能在建立网站连接之前退出。服务器的这些拒绝不会自动编码成一份HTTP 403发回浏览器;本章承载的是SOCKS后的TLS字节通道,VLESS基础响应也没有通用HTTP式状态码。浏览器可能只看到EOF、复位或等待结束。

若用户和Flow通过,目标解析仍可能失败。也可能域名解析到被固定版本默认final规则阻止的地址,freedom进入黑洞等待及关闭路径。这样的等待与网络丢包造成的等待在应用表象上相似,原因却不同。能够完成代理Encryption握手,并不证明远端DNS、目标规则和网站路径均已可用。

目标TCP成功后,网站也可能立即关闭、拒绝某种TLS参数,或在握手时返回TLS告警。代理只转发这些字节;浏览器的TLS实现判断它们与自己的握手状态是否一致。若返回证书不覆盖example.com、信任链不成立或Finished不匹配,浏览器应按网站TLS规则拒绝。更换VLESS UUID或HTTP承载模式不会修复一个真实的网站证书名称错误。

最后考虑已经进入应用阶段的中断。B断开会切断转发关系,即使C尚未立刻被网站关闭;各段的清理需要本地任务继续传播。客户端若重新建立B,并不自动恢复原网站TLS连接的全部序号、缓冲、HTTP响应位置和C上的状态。缓存Encryption票据最多帮助恢复该安全层的新连接,不是网站TCP会话迁移协议。

已确认的事件仍不能据此排除的失败
本地SOCKS成功B拨号失败、所有后续层失败
TCP B成功Encryption或VLESS失败、远端目标失败
Encryption完整交换通过用户/Flow拒绝、远端DNS或网站失败
VLESS请求被接受目标被规则阻止、目标TCP/TLS失败
网站TCP C建立网站证书、握手、HTTP应用失败
浏览器验证网站TLS成功应用授权失败、后续超时、中途截断

表中的“确认”需要对应证据,不能把其他层的日志相似词当作证明。TCP B的ACK确认运输字节,Encryption Tag验证某条记录,VLESS用户查找确认账号,网站Finished验证网站握手摘要;它们各自覆盖的对象不同。

本节是按源码控制流构造的故障分析,没有声称观察过这些故障的实际日志。重试、拒绝与清理可分别定位到固定VLESS outbound、VLESS inbound和freedom。

25.11 从配置推到报文,哪些结论需要额外证据 ​

配置描述一组策略和材料,源码把它们变成状态,状态再产生网络字节。三者不是一一逐字段复制的关系。UUID可以转换为原始16字节,flow可以转换为Protobuf Addon;但network: raw不会作为ASCII字符串写进VLESS头,encryption中的1rtt也不是某个单字节线上标志。

起点构建或运行时转换可在什么层观察结果
id字符串UUID解析、用户对象及查找映射VLESS解密请求中的16字节;原始ID另进入Vision状态
flow字符串账号策略、Addon编码、Vision包装18字节Addon及后续Vision块;不是每包重复整个请求头
静态公钥/私钥文本Base64URL解码、X25519密钥对象静态交换份额与派生结果;私钥不应直接上网
1rtt不采用缓存恢复的客户端行为完整临时交换路径,而非一个同名文本字段
native不启用额外XOR外观包装初期记录头及切换后路径,仍受其他包装影响
example.com:443SOCKS内部目标再编码VLESS端口01 bb、域名类型02及11字节名称
network/security选择拨号器与外层包装TCP B是否另有HTTP、TLS或REALITY握手
服务端freedom目标解析、final检查、系统拨号、双向复制TCP C及对应远端解析行为

因此,源码可以可靠确定52字节VLESS头及字段顺序,也可以确定某分支每条Encryption记录增加21字节;却不能单靠静态JSON确定随机IV、临时公钥、ML-KEM密文、Padding抽样、TCP分段或最终密码套件。它们依赖随机源、运行时输入、另一端选择和系统调度。教材只对实际复算过的构造密文给确定值,不为本章假装生成一份真实连接的完整密文抓包。

反向阅读时也要守住层次。如果网络观察者只看见17 03 03开头的记录,不应直接认定其内部必然是标准TLS;第22章Encryption使用相似外观,却有自己的密钥派生和记录语义。若在Direct后的native路径看见网站TLS记录,也不能倒推之前没有代理握手。不同阶段可以在同一TCP连接中采用不同处理状态。

改变配置时,必须重新检查组合,而不是把已解释的结构机械相乘。给RAW加TLS或REALITY,会在Encryption之下另建安全包装,Vision不会因此获准双重穿透;改成XHTTP,会先需要可工作的HTTP逻辑通道,再在其上进行Encryption和VLESS,HTTP分段、元数据与XMUX按第24章解释。删除Encryption同时保留RAW与security: none,则既失去本章的安全层,也可能触发该提交对公共VLESS端点的安全配置拒绝,不能继续沿用本章握手图。

版本核对尤为必要。本章简化出站字段、CommonConn的Vision分支、默认final规则与当前安全检查都是固定提交行为。别的内核支持同名协议,不代表实现了相同版本的Encryption、Vision包装或XHTTP布局。客户端能够导入一份配置,只能证明其解析了某些字段;还要确认未忽略关键字段、实际选中预期分支,并与服务端格式相容。

本章证据范围为:两份完整JSON通过语法解析;公开RFC X25519材料的32字节长度与Base64URL表示核对;SOCKS18字节、Vision VLESS52字节及首批记录长度独立计数;两端构建器、拨号顺序、CommonConn/Vision分支、响应缓冲与freedom目标检查按固定源码交叉阅读。沿用前章对公开密码材料的内部复算证据,但没有新增编译后的Xray两端互通、浏览器抓包或性能测量。

静态阅读能解释“代码在这些前提下会执行什么”,互通观察能补充“这次运行实际走了哪条分支”,密码规范与分析则限制“可以主张什么安全性质”。将这三类证据分开,才能在遇到新版本或新协议时,沿相同的方法判断它传什么、信任谁、保存什么状态,以及如何处理失败。下面转向其他代理协议,比较它们把地址、认证、加密与承载安排在哪一层。

对应源码入口集中在infra/conf/vless.go、proxy/vless、proxy.go与freedom.go。

从字节与状态机出发,理解每一层的职责。