NaïveProxy 
NaïveProxy 使用 Chromium 的网络栈来伪装流量,具有强大的抗审查能力和低可检测性。复用 Chrome 的网络栈也确保了在性能和安全性方面的最佳实践。
通过使用 Chromium 的网络栈,可以缓解以下流量攻击:
- 网站指纹识别 / 流量分类:通过 HTTP/2 中的流量复用 和模仿前导部分来缓解。
- TLS 参数指纹识别:通过复用 Chrome 的网络栈 来击败。
- 主动探测:通过 应用前置(application fronting)来击败,即将代理服务器隐藏在一个常用的前置服务器之后,并进行应用层路由。
- 基于长度的流量分析:通过填充和分片来缓解。
架构
[浏览器 → Naïve 客户端] ⟶ 审查者 ⟶ [前置服务器 → Naïve 服务器] ⟶ 互联网
NaïveProxy 使用 Chromium 的网络栈,在常规 Chrome 浏览器和标准前置服务器之间模仿流量。
前置服务器可以是任何知名的反向代理,能够基于 HTTP 授权头路由 HTTP/2 流量,从而防止对代理存在性的主动探测。已知的包括带有 forwardproxy 插件的 Caddy 和 HAProxy。
此处的 Naïve 服务器作为正向代理和包长度填充层工作。Caddy forwardproxy 也是一个正向代理,但它缺少填充层。一个 分支 将 NaïveProxy 填充层添加到 forwardproxy 中,将两者合二为一。
下载 NaïveProxy
下载此处。支持的平台包括:Windows、Android(需配合 Exclave、husi、NekoBox)、Linux、Mac OS 以及 OpenWrt(支持状态)。
用户应始终使用最新版本,以保持签名与 Chrome 一致。
从源码构建:请参阅 .github/workflows/build.yml。
服务器设置
以下描述的是 Caddy forwardproxy 设置的 naïve 分支。
下载此处或从源码构建:
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
~/go/bin/xcaddy build --with github.com/caddyserver/forwardproxy=github.com/klzgrad/forwardproxy@naive
Caddyfile 示例(请相应替换 user 和 pass):
{
order forward_proxy before file_server
log {
exclude http.log.error # Avoid logging user activity
}
}
:443, example.com {
tls me@example.com
encode
forward_proxy {
basic_auth user pass
hide_ip
hide_via
probe_resistance
}
file_server {
root /var/www/html
}
}
:443 必须出现在最前面,此 Caddyfile 才能正常工作。请参阅 Caddyfile 文档 以自定义 TLS 证书。对于更高级的用法,请考虑使用 Caddy 2 配置的 JSON。
使用 Caddyfile 运行:
sudo setcap cap_net_bind_service=+ep ./caddy
./caddy start
另请参阅 Systemd 单元示例 和 HAProxy 设置。
客户端设置
使用以下 config.json 运行 ./naive,以在本地端口 1080 获取 SOCKS5 代理。
{
"listen": "socks://127.0.0.1:1080",
"proxy": "https://user:pass@example.com"
}
或者使用 quic://user:pass@example.com,如果它效果更好。另请参阅 参数用法 和 性能调优。
第三方集成
- v2rayN,GUI 客户端
下游注意事项
不要使用 master 分支来跟踪更新,因为每次新的 Chrome 发布都会从新的根提交进行 rebase。请使用稳定版本及其关联的标签来跟踪新版本,其中也提供了简短的发布说明。
Padding 协议,一份非正式规范
该填充协议的设计选择了低开销和更简单的实现方式,其理念是,大量涌现的、可弃用的、临时拼凑的规避协议设计,比精巧的设计更能从后勤上阻碍审查研究。
代理负载填充
NaïveProxy 通过 HTTP/2(或 HTTP/3)CONNECT 隧道代理双向流。双向流以一系列的数据读取和写入操作运行。在流建立后,双向流中的前 kFirstPaddings(8)次读取和写入采用此格式进行填充:
struct PaddedData {
uint8_t original_data_size_high; // original_data_size / 256
uint8_t original_data_size_low; // original_data_size % 256
uint8_t padding_size;
uint8_t original_data[original_data_size];
uint8_t zeros[padding_size];
};
padding_size 是 [0, kMaxPaddingSize](kMaxPaddingSize:255)范围内均匀分布的随机整数。original_data_size 不能大于 65535,否则必须拆分为多次读取或写入。
kFirstPaddings 被选为 8,以平滑由常见初始握手形成的包长度分布峰值:
- 常见客户端初始序列:1. TLS ClientHello;2. TLS ChangeCipherSpec, Finished;3. H2 Magic, SETTINGS, WINDOW_UPDATE;4. H2 HEADERS GET;5. H2 SETTINGS ACK。
- 常见服务器初始序列:1. TLS ServerHello, ChangeCipherSpec, ...;2. TLS Certificate, ...;3. H2 SETTINGS;4. H2 WINDOW_UPDATE;5. H2 SETTINGS ACK;6. H2 HEADERS 200 OK。
kFirstPaddings 之后的后续读取和写入不进行填充,以避免性能开销。此外,后续包长度通常被认为信息量较少。
H2 RST_STREAM 帧填充
在实验中,NaïveProxy 倾向于在每个会话中发送过多的 RST_STREAM 帧,这是一种常规浏览器中不常见的行为。为了解决这个问题,一个总长度分布在 [48, 72] 之间的 END_STREAM DATA 帧被填充并前置到 RST_STREAM 帧之前,使其看起来像一个 HEADERS 帧。服务器通常会对此回复一个 WINDOW_UPDATE,因为填充部分计入流量控制。这是否会导致新的不常见行为目前尚不清楚。
H2 HEADERS 帧填充
CONNECT 请求和响应帧过短且不常见。为了使长度与真实的 HEADERS 帧相似,一个 padding 头部被填充了一串未进行 Huffman 编码且伪随机程度足以避免被索引的符号。填充序列的长度随机分布在 [16, 32](请求)或 [30, 62](响应)之间。
填充协议的可选加入
NaïveProxy 客户端应与任何不了解此填充协议的常规 HTTP/2 代理互操作。NaïveProxy 服务器(即任何支持此填充协议的代理服务器)应与任何不了解此填充协议的常规 HTTP/2 客户端(例如常规浏览器)互操作。
NaïveProxy 服务器和客户端通过 CONNECT 请求和响应中是否存在 padding 头部来确定对端是否支持此填充协议。仅当存在 padding 头部时,填充协议才会启用。
对服务器的首个 CONNECT 请求无法使用 "Fast Open" 在响应之前发送负载,因为服务器的填充能力尚未从首个响应中确定,且未知对于 Fast Open 应发送带填充还是不带填充的负载。
与 Chromium 上游的差异
- 最小化源代码和构建体积(为原始大小的 0.3%)
- 禁用异常和 RTTI,Mac 和 Android 除外。
- 支持 OpenWrt 构建
- (Android、Linux)使用内置验证器而非系统验证器(在 Linux 上移除对 NSS 的依赖),并按照 Go 在 crypto/x509/root_unix.go 和 crypto/x509/root_linux.go 中的行为读取系统信任存储:
- 环境变量 SSL_CERT_FILE 中的文件
- 以下第一个可用文件
- /etc/ssl/certs/ca-certificates.crt (Debian/Ubuntu/Gentoo 等)
- /etc/pki/tls/certs/ca-bundle.crt (Fedora/RHEL 6)
- /etc/ssl/ca-bundle.pem (OpenSUSE)
- /etc/pki/tls/cacert.pem (OpenELEC)
- /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem (CentOS/RHEL 7)
- /etc/ssl/cert.pem (Alpine Linux)
- 环境变量 SSL_CERT_DIR 目录中的文件
- 以下第一个可用目录中的文件
- /etc/ssl/certs (SLES10/SLES11, https://golang.org/issue/12139)
- /etc/pki/tls/certs (Fedora/RHEL)
- /system/etc/security/cacerts (Android)
- 处理 PKCS#7 格式的 AIA 响应
- 允许代理使用更高的套接字限制
- 强制所有套接字使用隧道
- 支持使用
fastopen头部的 HTTP/2 和 HTTP/3 CONNECT 隧道快速打开 - 填充 RST_STREAM 帧