TLS 是一个伟大的技术,它确保了网络传输的内容不被中间人篡改。现在越来越多的网站正在使用 HTTPS(即 HTTP over TLS)来保护网页内容。与此同时,TLS 设计中的一个缺陷,却使得阻断 TLS 连接变得可控。
在建立新的 TLS 连接时,客户端(如浏览器)发出的第一个握手包(称为 Client Hello)中,包含了想要访问的域名信息(称为 SNI,Server Name Indication)。某些服务器(比如 CDN)会同时支持多个域名,在加密传输之前,它需要知道客户端访问的是哪个域名。于是 SNI 必须以明文的方式传输。并且由于浏览器并不知道服务器是否需要 SNI,浏览器会对所有的 TLS 握手都加入 SNI。
于是,大家都懂的。根据黑名单,某些防火墙对于 TLS 连接可以进行精确地阻断。
目前 Mozilla 和 CloudFlare 主导了一项对 SNI 的改进方案,称为 Encrypted SNI (ESNI)。这个提案还在早期的讨论状态中,目测还需要两年时间才可以定稿和推广。现阶段只有 Firefox Nightly(客户端),以及 CloudFlare 和 Wikipedia(网站)支持初代的 ESNI。
在 ESNI 正式推广之前,我们还需要其它的技术来避开对于 TLS 连接的探测。
目前我们还没有发现通用的解决方案。有一个较为通用的,但部署起来略麻烦的方案称为 Domain Fronting。它的原理简单来说是这样的:部分服务器允许 TLS 连接说自己需要域名 A,但之后的 HTTP 协议说自己需要域名 B。或者服务器压根就不看 SNI 的信息。在这种情况下,对于一个黑名单的域名, 我们在建立 TLS 的时候,可以选用一个不在黑名单的域名,绕过对 TLS 连接的监测。
当然,它的缺点是,依赖于服务器行为。也就是说,每个不同的站点,可能都需要不同的策略(域名)。
举两个例子:
- P 站的服务器实际上是不看 SNI 的。在建立 TLS 连接的时候,即使不携带 SNI,也可以正常进行 HTTPS 访问。
zh.wikipedia.org
是一个黑名单域名,但同站点的www.wikipedia.org
就不是。我们在建立 TLS 连接时,使用 SNI =www.wikipedia.org
,之后的 HTTP 请求依然可以正常连到zh.wikipedia.org
。
顺便说一句,Domain Fronting 实际上不是一个合理的用法,网站完全可以拒绝这类连接,比如 Google 和 Amazon 就主动在自己的所有服务中拒绝这项技术。即使这样,Domain Fronting 依然是在 ESNI 之前最好的绕过技术。
好了,接下来介绍一下 V2Ray 中如何使用 Domain Fronting。对的,作为一个超级复杂,超级难用的工具,如果连 Domain Fronting 都不支持,就愧对它的名声了。
所需的技能:TLS 配置、dokodemo-door、freedom、路由和对 MITM 的初步了解。
大概的工作流程:本地的 V2Ray 拦截浏览器的 TLS 连接,让浏览器以为自己已经连上了目标服务器,读取 TLS 中承载的 HTTP 数据;然后自己与目标服务器建立一个新的 TLS 连接,把 HTTP 数据发过去。也就是说,你不需要代理服务器,就可以绕过(部分) SNI 封锁了。
第一步,你需要一个自签的 CA 证书。当然如果你能弄到正规机构签发的 CA 证书,那就更省力了。签发的过程就不详述了,其它各种文章里都搜得到。签发完成之后,你需要把证书导入进系统或者浏览器,从而让浏览器信任由此 CA 签发的网站证书。
第二步,你需要配置一个 dokodemo-door 用于拦截 TLS 连接。需要开启 security: "tls"
,需要配置签发证书。样例如下:
{
"listen": "0.0.0.0",
"port": 443, // 其它端口也可以,但 443 比较方便
"tag": "df-in",
"protocol": "dokodemo-door",
"settings": {
"network": "tcp",
"address": "1.1.1.1", // 不重要,但是要写
"port": 443,
"followRedirect": true // 一定要写
},
"streamSettings": {
"security": "tls",
"tlsSettings": {
"alpn": ["h2"],
"certificates": [
{
"usage": "issue", // 重要
"certificateFile": "/path/to/ca.cer", // 刚刚签发的 CA 证书
"keyFile": "/path/to/ca.key"
}
]
}
}
}
第三步,把浏览器发出的连接转发到上述的 dokodemo-door。你可以选择透明代理,或者强行 hosts,都是没有问题的。
第四步,配置一个 freedom,用于建立指向服务器的 TLS 连接。样例如下:
{
"protocol": "freedom",
"tag": "no-sni-out",
"settings": {
"domainStrategy": "UseIP"
},
"streamSettings": {
"security": "tls",
"tlsSettings": {
"allowInsecure": true, // 不指定 SNI,就必须打开这一项。
"alpn": ["h2"]
}
}
}
第五步,把 dokodemo-door 和路由连起来。比如:
{
"inboundTag": ["df-in"],
"domain": ["geosite:pixiv"],
"outboundTag": "no-sni-out",
"type": "field"
}
第六步,运行 V2Ray,然后测试一下:
curl --resolve www.pixiv.net:443:127.0.0.1 -I https://www.pixiv.net/
如果配置成功的话,可以看到类似下面的信息:
HTTP/2 200
server: nginx
好了,简单介绍到这里,喜欢折腾的同学可以玩一玩。需要注意的是,被 SNI 阻断的域名,大多数都被 DNS 污染了,你需要先解决 DNS 污染(也可以用 V2Ray 哟),才可以接着解决 SNI 阻断。
Domain Fronting 实际上并不要求 MITM,只要实时修改 TLS 握手信息即可。但 V2Ray 还不支持这么做,只能通过上述的方式变向去支持它。由于 Domain Fronting 的应用场景越来越小,我们也就不打算花很多精力去简化它的配置了。
Congratulations @v2ray! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Vote for @Steemitboard as a witness to get one more award and increased upvotes!