Skip to content

如何适配站点

URenko edited this page May 27, 2023 · 4 revisions

我们需要先调查墙使用了何种检测/封锁方式,然后才能对症下药。有时是同时使用了多种检测/封锁方式,需要按照顺序检查并应用相应的对策。

在下面,我们假设无法访问的网站为sub.example.com

DNS污染

浏览器在建立连接前,需要先将域名解析为IP地址,这是通过查询DNS服务器完成的,因此墙可以返回一个错误的结果。

以Windows系统为例,在命令提示符中运行如下代码可以得到解析的IP地址:

nslookup sub.example.com

可以到如ip138等网站查询这一IP地址的归属,如果查到的是如Facebook等与sub.example.com毫无关系的归属,说明遭到了DNS污染。

为了解决DNS污染,我们需要网站得真实IP地址。在能访问 https://dns.google 等网站的情况下,我们可以去这些网站查询真实IP地址。或者也可以观察 https://ping.chinaz.com 等网站的境外结果变相得到真实IP地址。

真实IP测试

在得到真实IP地址的情况下,我们可以修改系统中的hosts文件然后尝试通过浏览器直接访问(需要注意,如果浏览器开启了如DNS-over-HTTPS (DoH) 等加密DNS协议,有可能忽略hosts文件,需要手动配置)。

或者也可以通过curl测试,如假设上面得到真实IP为1.2.3.4,则可通过如下命令测试:

curl https://sub.example.com --resolve "sub.example.com:443:1.2.3.4" -v

如果能正常访问,则可以通过配置hosts或DNS-over-HTTPS(DoH)、DNS-over-TLS(DoT)等加密DNS协议来访问。这种情况就不需要使用Accesser(尽管因为Accesser自带加密DNS也是可用的)。

如果出现“无法建立安全连接”、failed to receive handshake, SSL/TLS connection failed等错误,那么很可能就是遇到了SNI RST,这就需要使用Accesser。

SNI RST

在现在流行的TLS协议中,建立连接过程的client hello没有被加密,其中的server_name字段就是域名,易被识别,然后墙会发出TCP RST以结束连接。

除了上面的测试外,如果您懂的如何抓包,可以观察到在客户端发出client hello后马上收到TCP RST(而不是正常的TLS错误)。 进一步地,可以往其他境外IP地址发送包含sub.example.com的client hello也会出现TCP RST。

ECH

为了解决TLS握手未加密的问题,互联网工程任务组(IETF)先是提出了Encrypted Server Name Indication (ESNI),然后又提出了Encrypted Client Hello (ECH) 作为TLS的一个新扩展。

目前,ECH尚未大规模应用,新的浏览器开始支持,但可能需要手动开启,同时需要配合上文提到的境外DoH使用。请自行搜索自己浏览器的设置方法。

HTTP/3

HTTP/3是基于UDP的,因此不会被RST。但现在浏览器一般会先尝试HTTP/2,然后依据Alt-Svc尝试升级。DNS SVCB/HTTPS是解决方案。

Accesser

在RFC文档中指出,server_name字段并非必须的,而是可选项,因此我们可以选择不发送server_name,这就是Accesser目前的原理。

为了使用Accesser,如果您使用PAC来进行代理,可以在PAC文件(如果工作目录中没有这个文件,可以从 https://github.com/URenko/Accesser/blob/master/accesser/pac 下载)中仿照其中的内容添加example.com(需要尤其小心文件中的逗号):

  ...
  "wikipedia.org": 1,
  "example.com": 1
};

如果不便使用PAC代理,或只是想做一个简单的测试,可以使用curl:

curl https://sub.example.com -x http://localhost:7654 -v --insecure

此时可能成功,说明已经可用!

也可能在Accesser的控制台中观察到这样的错误,如:

("hostname 'sub.example.com' doesn't match either of '*.abc.xyz', '123.abc.xyz', 'abc.xyz'",)

说明服务器返回的HTTPS证书并不和sub.example.com,我们可以在config.toml中设置check_hostname = false(不推荐)。 或者我们可以在'*.abc.xyz', '123.abc.xyz', 'abc.xyz'中挑选一个没被墙的域名,如abc.xyz,然后用abc.xyz作为校验用的域名(由于一般sub.example.comabc.xyz是有关联的,如同属一家公司,因此安全性可以保障),可以在config.toml[alter_hostname]字段添加一条规则:

"sub.example.com" = "abc.xyz"

需要注意,这里添加的abc.xyz不仅会作为校验用的域名,也会作为server_name在client hello中发送,这就是要求这个域名没被墙的原因(一般总能找到)。

还有一种情况,sub.example.com使用了CDN,而CDN一般需要server_name来识别是何网站,因此我们需要发送这一网站别的可接受的server_name,这也是通过[alter_hostname]字段配置,具体见config.toml中的例子。如果没有合适的server_name(例如Cloudflare),应当考虑前面提到的更好的技术(ECH、HTTP/3)。

其他

  • 有可能出现网站的部分IP地址无法连接,如维基媒体。这时Accesser内置的DNS返回的IP不一定能连接,此时需要配置config.toml中的[hosts]字段指定IP地址。
  • SNI不一定用于TCP RST,也可能用于间歇性的丢包,如GitHub,这一问题不易确认清楚,但也可先配置好Accesser使用一段时间观察效果。