在国内打开 https://www.google.com,浏览器转了一会儿,报「连接已重置」或者干脆超时;git clone GitHub 上的仓库,时快时慢。这不是网速慢,而是请求在路上被识别出来、拦下了。

难的是拦截不止一处,翻越的工具也跟着一代代地变:Shadowsocks、V2Ray、Xray、Trojan、Clash、sing-box……名字一大堆,配置各不相同。配置可以照着文档或者问 AI,但不知道每一代在躲什么,就分不清该选哪个,出了问题也看不懂日志。

这篇顺着「打开 www.google.com」这一次请求回答三个问题:封锁发生在哪里,代理协议为了躲开什么一代代演化,本机的客户端又做了什么。最后讲同一套隧道原理的另一个用途:用 Tailscale 从外面连回家里的电脑。读之前最好知道一次请求要经过 DNS、TCP、TLS 这几步,不熟的话可以先看 Life of a Request。

一次请求在哪里被拦下

打开 https://www.google.com,浏览器要先查 DNS 拿到 IP,再建 TCP 连接,然后做 TLS 握手,最后才在加密通道里发 HTTP 请求。前三步里都有不加密的信息,审查系统(通常称为防火长城,Great Firewall,简称 GFW)不需要解密任何东西,看这些明文就够了:

审查者不需要解密,只看 DNS 查询和 TLS 握手里的明文域名就够了

手段

看的是什么

你看到的现象

DNS 污染

明文 DNS 查询里的域名

解析出的 IP 不对,连不上

IP 封锁

数据包的目的 IP

连接超时

SNI 检测

TLS 握手第一个包(ClientHello)里明文的服务器名(SNI)

「连接已重置」

这几种手段都有公开的测量研究:

  • DNS 污染的规模:GFWatch 项目连续九个月、平均每天测试 4.11 亿个域名,共发现 31.1 万个被 DNS 过滤的域名;注入的假地址里甚至有 Facebook、Dropbox、Twitter 这些美国公司的 IP。

  • SNI 一直在被看:TLS 1.3 曾有加密 SNI 的扩展(ESNI),2020 年 7 月 29 日起,带这个扩展的 TLS 连接在国内被整体封锁。2024 年 4 月 7 日起,基于 UDP 的 QUIC(HTTP/3 用的协议)也开始按域名被封:QUIC 的握手包虽然加密,但密钥能从包里推导出来,GFW 会大规模解密握手包读出 SNI。

所以只换一个干净的 DNS 不够:IP 和 SNI 照样会暴露目的地。要躲开全部三处,得把「我要去哪」整个藏起来。

代理:把请求装进隧道

代理的思路是:本机不直接连 Google,而是和一台境外服务器建一条加密隧道,把「我要连 www.google.com:443」这句话和后面的数据都装进去。服务器替你查 DNS、连 Google,再把结果原路送回。

审查者只看到本机和一台境外服务器之间的加密流量,看不到要去哪

这样前面三种手段都落了空:DNS 在境外查,Google 的 IP 和 SNI 都在隧道里面。审查者能看到的,只剩「这台电脑在和某个境外 IP 传加密数据」。

浏览器和本机客户端之间说的是标准的代理协议,SOCKS5 或 HTTP 的 CONNECT 方法:浏览器告诉客户端「我要连 www.google.com 的 443 端口」。SOCKS5 允许直接把域名交给代理(RFC 1928),由代理那一头去解析,本机的 DNS 被污染也影响不到它。这一点在终端里尤其要注意,后面「让终端也走代理」一节会讲到。

还有一点要清楚:代理服务器的运营方能看到你访问了哪些域名。HTTPS 的内容它看不到,但目的地它是知道的。这也是有人宁愿自己搭服务器的原因。

道高一尺:代理协议怎样一代代演化

隧道本身也会被发现。回头看这十几年的协议,每一代都在躲上一代被识别的方式:

方案

外面看起来像什么

怎么被识别,代价是什么

传统 VPN(OpenVPN、WireGuard 等)

一眼就能认出的 VPN 协议

握手格式固定,比如 WireGuard 握手的第一个包,以类型号 1 加三个 0 字节开头;WireGuard 官方明说它不做混淆

Shadowsocks、VMess

一串随机字节,「什么都不像」

首包的长度和熵引来主动探测;2021 年 11 月起,「什么都不像」本身也成了特征

Trojan、VLESS + TLS

访问一个正常的 HTTPS 网站

要有自己的域名和证书;TLS 里再套一次 TLS 握手,包的大小和来回次数有规律

VLESS + Vision + REALITY

访问一个真实存在的国外大网站

不用自己的域名;探测者看到的是那个网站的真证书

Hysteria 2、TUIC

HTTP/3(QUIC)流量

跑在 UDP 上,适合丢包严重的网络;有的网络对 UDP 限速,QUIC 的 SNI 也会被读

下面拆开讲中间三次关键的转折。

第一步:加密,让内容看不见

最早的办法是直接用 HTTP 或 SOCKS 代理连到境外,但这些协议是明文的,请求内容一览无余。2012 年,一位网名 clowwindy 的开发者发布了 Shadowsocks:客户端和服务器共享一个密码,从第一个字节起就全部加密,外面看到的只是一串随机字节。后来 V2Ray 项目的 VMess 协议也是这个思路。

但「什么都不像」本身就是特征。2020 年的一项研究发现,GFW 先根据每个连接第一个数据包的长度和熵(随机程度)怀疑某台服务器在跑 Shadowsocks,再派出主动探测:把之前截获的合法首包原样或改几个字节重放过去,或者发一串随机字节,看服务器怎么反应。不同实现对这些探测的反应(重置连接、超时、返回数据)和普通服务器不一样,于是露了馅。

2021 年 11 月起,GFW 又上线了纯被动的检测。它并不直接定义什么是「全加密流量」,而是用一组粗糙但高效的规则,先把不太像全加密流量的连接放行:首包里 1 的比特所占比例明显偏离一半的(真正随机的数据接近一半),开头几个字节是可打印字符的,可打印字符占了一半以上的,或者能认出是常见协议的。剩下没被放行的连接就封锁。Shadowsocks、VMess、obfs4 都受到影响。

第二步:伪装,让流量像一个正常网站

既然「什么都不像」可疑,那就像一个最常见的东西:HTTPS。Trojan 和 VLESS + TLS 的做法是在服务器上用自己的域名、真实的证书跑一个标准的 TLS 服务。外面看来,你只是在访问一个普通网站;探测者直接来访问,服务器会把它当成普通访客,「回落」到一个真的网页上。

这里的 VLESS 是 Xray 项目的一个很轻的协议,本身不负责加密,安全性交给外层的 TLS。

代价有两个:一是要有自己的域名和证书,域名本身也可能被封;二是一个新的特征。你在浏览器里访问的网站本身就是 HTTPS,于是隧道里还有一次 TLS 握手,叫作 TLS in TLS。握手阶段那几个包的大小和来回次数很有规律,2024 年的一项研究在一个上百万用户的运营商网络里,靠「被封装的 TLS 握手」这个特征可靠地识别出了多种混淆代理,即使加了随机填充和多层封装。

第三步:借用,连域名都用别人的

2023 年,Xray 项目推出了 REALITY。它让握手里的 SNI 写的是一个真实存在的国外大网站,自己不需要域名,也不需要证书:

REALITY 只认持有密钥的客户端,其余连接看到的都是真实网站和它的真证书

服务器只对握手里带着正确密钥的客户端,回一张临时签发的证书;其他连接,包括 GFW 的探测,都被原样转发给那个真实的网站,拿到的是真网站的真证书。从外面看,这台服务器的表现就是一个端口转发。

配套的 Vision 流控对付的是上一步的 TLS in TLS:它给里层的 TLS 握手加上随机填充;里层是 TLS 1.3 时,握手之后直接转发里层已经加密的数据,不再加密第二遍。

另一条路是换到 UDP 上。Hysteria 2 基于改造过的 QUIC,外面看是 HTTP/3 流量,服务器也能像普通网站一样回应 HTTP 请求。它的 Brutal 拥塞控制按你设定的带宽发送,不因丢包降速,专为丢包严重、不稳定的网络设计。代价是有的网络对 UDP 限速,而前面说过,QUIC 握手里的 SNI 也会被读。

没有一劳永逸的协议。判断一个方案,看三件事:外面看起来像不像正常流量,被探测时露不露馅,出了问题你看不看得懂它的日志。

本机客户端做了什么

分流:只有该绕路的才绕路

客户端最重要的工作是分流:访问国内网站直连,只有被封锁的才走代理。这样国内网站不受影响,也不会把所有流量都交给代理服务器。

规则一般按域名(比如社区维护的域名列表)、按 IP 归属地(GeoIP),有的客户端还能按发起连接的程序来分。v2rayN 的日志里能看到每一个连接最后去了哪里,比如在「绕过大陆」的路由下:

text
alive.github.com:443 [http -> proxy]
p2plog.douyucdn.cn:443 [http -> direct]

DNS:fake-ip 为什么能同时解决几个问题

在 TUN 模式下(下一节讲),程序会先自己查 DNS,再去连接查到的 IP。这时候如果用本机的 DNS 查 www.google.com,查到的可能是被污染的假地址;就算是真地址,客户端也只拿到一个 IP,不知道它对应哪个域名,没法按域名分流。

很多客户端用 fake-ip 解决这个问题:

fake-ip 先回一个假地址,等应用真正连接时,客户端就知道它要去哪个域名

客户端立刻回一个保留网段里的假地址(mihomo 默认是 198.18.0.1/16),并记住「这个假地址就是 www.google.com」。应用拿着假地址来连接时,客户端查表就知道目的地是哪个域名,可以按域名分流,再把域名交给代理服务器去解析。本机的 DNS 污染碰不到它,DNS 查询也不用等一个来回。

系统代理与 TUN 模式

客户端一般提供两种接管流量的方式:

模式

原理

优点

缺点

系统代理

设置操作系统的代理选项,遵守这个设置的应用把请求交给客户端

简单,对系统影响小

不遵守系统代理的应用(不少命令行工具、游戏)不会走代理

TUN 模式

创建一块虚拟网卡,修改路由,接管本机所有的网络流量

所有应用都能分流,不用逐个配置

会和其他同样接管路由的软件(比如 VPN、后面讲的 Tailscale)冲突

再进一步,可以在路由器上做透明代理,让家里所有设备都不用单独配置,参见 V2Ray 文档中的 透明代理 一节。

让终端也走代理

浏览器会自动使用系统代理,但终端里的命令行工具(git、curl、pip 等)一般不会,要用环境变量告诉它们代理的地址。我用的 v2rayN 当时(6.x 版)在本机 10808 端口提供 SOCKS5 代理、10809 端口提供 HTTP 代理,实际的端口以客户端界面上显示的为准:

bash
export ALL_PROXY=socks5h://127.0.0.1:10808

注意是 socks5h 而不是 socks5:socks5:// 让 curl 在本机解析域名,正好撞上 DNS 污染;socks5h:// 把域名原样交给代理去解析。环境变量只对这个终端里之后启动的程序生效,原理见 常用开发工具 里讲 Shell 和镜像与代理的两节。

开源工具

代理软件一般分成两层:内核实现协议、路由和 DNS,既能当客户端,也能当服务端;图形客户端负责管理服务器列表和规则、切换系统代理和 TUN,内部调用某个内核。下面这些都是开源的:

内核

许可证

特点

Xray-core

MPL-2.0

VLESS、Vision、REALITY 的出处,2020 年从 V2Ray 分出

V2Ray(v2fly/v2ray-core)

MIT

VMess 的出处,Project V 的社区延续

sing-box

GPL-3.0 或更新版本

通用代理平台,支持的协议很多,包括 Hysteria 2、TUIC,也有官方的图形客户端

mihomo

GPL-3.0

即 Clash Meta 内核,沿用 Clash 的规则格式

Hysteria

MIT

Hysteria 2 协议的官方实现

shadowsocks-rust

MIT

Shadowsocks 的 Rust 实现

图形客户端

平台

用的内核

v2rayN

Windows、Linux、macOS

Xray、sing-box 等

v2rayNG

Android

Xray、v2fly

Clash Verge Rev

Windows、macOS、Linux

mihomo

sing-box 官方客户端

Android、Apple 平台、桌面

sing-box

服务器(常说的「节点」)从哪里来,有两条路:在一台境外服务器上自己部署上面任意一个内核的服务端,或者使用别人提供的服务。自己搭的好处是只有自己用,不用把全部的访问记录交给不认识的人;代价是要自己维护,服务器的 IP 也可能被封。

同一套原理的另一面:用 Tailscale 连回家

加密隧道不只能用来「出去」,也能用来「回家」。常见的场景是:人在外面,想访问家里的 NAS、台式机,或者家里跑着的开发服务。难点在于家里的电脑通常没有公网 IP:它在路由器的 NAT 后面,运营商往往还套了一层 NAT,外面的连接根本找不到它。

有三类做法:

做法

原理

代价

公网 IP + 端口映射

让路由器把某个端口转给家里的电脑

服务直接暴露在公网上,很多宽带也拿不到公网 IP

反向隧道,比如 frp

家里的电脑主动连到一台有公网 IP 的服务器,外面的访问由它转进来

要一台公网服务器,所有流量都经过它

组网,比如 Tailscale

每台设备之间建 WireGuard 加密隧道,尽量点对点直连

每台设备都要装客户端

Tailscale 怎么做到点对点

Tailscale 把这件事拆成了控制面和数据面:

协调服务器只交换公钥和地址,数据尽量点对点直连,实在打不通才经中继
  • 数据面是 WireGuard:每台设备有一对密钥,设备之间用对方的公钥建立加密隧道。WireGuard 用 Noise 协议框架做握手,用 ChaCha20-Poly1305 加密。前面说它「一眼就能认出」,用来回家正合适:这里要的是安全和简单,不是伪装。

  • 控制面是协调服务器:用 Tailscale 自己的话说,它是一个「共享的公钥投递箱」。它负责分发公钥、给每台设备分配一个 100.x.y.z 的地址(来自运营商级 NAT 专用的 100.64.0.0/10 网段),以及管理谁能访问谁。你的数据不经过它。

  • NAT 穿透:两台设备都在 NAT 后面,谁也不知道对方的公网地址和端口。每台设备先用 STUN 探测出自己在公网上的地址和端口,通过协调服务器和中继告诉对方,然后双方同时向对方发包,在各自的 NAT 上「打洞」。两边都是难打穿的 NAT 时,退回到 DERP 中继:中继只转发已经加密的数据,它没有私钥,解不开。

最常用的几个用法

功能

作用

MagicDNS

自动给每台设备一个名字,ssh 家里电脑的名字 就能连上,不用记 IP

子网路由(subnet router)

让一台设备当网关,把家里整个局域网接进来,没装 Tailscale 的设备(比如打印机)也能访问

出口节点(exit node)

把所有上网流量都从某台设备出去,比如人在国外时,让流量从家里的电脑出去

在国内用要注意

  • 中继离得远:截至 2026 年 10 月,Tailscale 官方的 DERP 中继没有中国大陆的节点,最近的在香港、东京、新加坡。打洞成功时不受影响;打洞失败、走中继时延迟会明显变高。可以在国内的服务器上自建 DERP(官方的 derper 程序,需要一个域名)。

  • 和代理客户端抢路由:Tailscale 和代理客户端的 TUN 模式都要改系统路由,同时开时可能互相干扰。出问题时,先让代理客户端对 100.64.0.0/10 网段直连,或者把它排除在 TUN 之外。

开源到什么程度

Tailscale 的节点软件是开源的(BSD-3-Clause),但 Windows 和 macOS / iOS 上的图形客户端、以及官方的协调服务器不开源。官方协调服务有永久免费的个人版(截至 2026 年 10 月,最多 6 个用户,每个用户的设备数量不限)。想完全自己掌控,可以用开源的 Headscale 自建协调服务器,它只支持一个网络,正好适合个人使用。如果你的两台机器里有一台有公网 IP,直接手写 WireGuard 配置也可以,只是不再有自动打洞和中继。

小结

回到开头那次打不开的 www.google.com:DNS 被抢答,IP 被封,SNI 被看见。代理把「要去哪」整个装进加密隧道,协议的演化则是在回答「隧道本身怎么不被认出来」:先加密,再伪装成 HTTPS,最后连网站都借用别人的。

封锁手段

看的是什么

绕过的思路

DNS 污染

明文的 DNS 查询

域名交给代理端解析(socks5h、fake-ip)

IP 封锁、SNI 检测

目的 IP 和明文的 SNI

装进隧道,外面只看到代理服务器

全加密流量检测

「什么都不像」

伪装成 HTTPS

主动探测

服务器被探测时的反应

对探测者表现得像一个真网站(回落、REALITY)

TLS in TLS

隧道里握手的大小和节奏

里层握手加填充(Vision)

按要做的事来选工具:

想做的事

用什么

访问被封锁的网站

代理:Xray、sing-box、mihomo 等内核,加一个图形客户端

从外面连回家里的设备

组网:Tailscale 或 Headscale,或者直接用 WireGuard

让别人从公网访问家里的服务

反向隧道:frp

一次请求在网络上的完整旅程,见 Life of a Request;终端、镜像和环境变量的原理,见 常用开发工具。

参考资料

审查机制的测量研究:

协议与工具:


全文评论

0

正在加载评论…