防火墙是一套复杂的网络监控和审查系统,旨在对互联网内容进行管理和控制。它通过多种技术手段,对进入的互联网流量进行过滤,以确保网络内容的合法性和安全性。SNI阻断和DNS劫持、污染以及ip封锁是常见的几种方式
最近 linux.do 被墙,开始只是dns污染,可以通过简单修改hosts实现访问,但后来sni被阻断,需要魔法或使用Doh,如果ip封锁那只能使用魔法了。
[!question]+什么是Doh
DNS over HTTPS(Doh) 是一种将 DNS 查询封装在 HTTPS 协议中的技术。传统的DNS查询以明文形式传输,这意味着:网络运营商可以看到你的DNS查询;中间人可以拦截和篡改DNS 响应;你的浏览历史可能被记录和分析。而DoH可以加密DNS查询以及SNI,来防止DNS劫持,中间人也不知道你访问的地址。
但是Doh能做的非常有限,就像始皇提供的Doh可以访问 linuxdo,却无法访问 google.com,这是因为 linuxdo 支持ECH可以拿到ECHconfig,google应该是多层并用:ip 黑洞/失联、SNI/Host DPI(含H3的QUIC SNI 对应策略)、有状态封锁等。就算 SNI 藏住了,也可能被 IP/路由层直接卡死。
Doh也只是隐藏 DNS 查询的一种方式,让你的 ISP(或者任何可能窥探的人)看不到。而获取网页有几个步骤——DNS 只是这个过程中的第一步,也是最容易被阻止和绕过的。防火墙更有效,因为它们会阻止你的电脑和服务器之间的流量,但可以通过绕过防火墙来绕过(比如使用 VPN 或代理服务器)。另一种阻止方法是使用 BGP 搞乱互联网路由表,但这通常会导致太多的附带损害。(就像伊朗试图阻止 YouTube,结果搞砸了中东的大部分地区)。
[!question]+什么是SNI阻断
现在99%网站都是使用了https(即 HTTP over TLS),在http基础上增加了tls协议,它确保了网络传输的内容不被中间人篡改,但tls设计中的一个缺陷,却使得阻断tls连接变得可控。
在建立新的tls连接时,客户端(如浏览器)发出的第一个握手包(称为 Client Hello)中,包含了想要访问的域名信息(称为SNI,Server Name Indication)。某些服务器(比如 CDN)会同时支持多个域名,在加密传输之前,它需要知道客户端访问的是哪个域名。于是SNI必须以明文的方式传输。并且由于浏览器并不知道服务器是否需要SNI,浏览器会对所有的TLS握手都加入 SNI。 防火墙可以根据明文SNI信息,对TLS连接进行精确阻断
阻断原理:TCP握手的流程并没有任何形式的加密(连接都没有建立怎么加密呢?),所以只要监听到了TCP连接的源IP、源端口、目的IP、目的端口(这些信息已经包含在三次握手中),任何人都可以伪装成服务端向我们发送伪造的RST数据包。这也正是防火墙阻断我们连接的技术:在建立TCP连接后,通过识别TLS Client Hello中的SNI信息,判断是否是黑名单网站,若是,则伪装成服务端向我们发送RST数据包,强行中断我们与真正服务端的连接。
如何判断SNI阻断
- 在浏览器中新建标签页
- 按下 F12 打开开发者工具,选择
网络/network面板 - 在地址栏中输入转到使用 HTTPS 的目标网址,等待页面加载完成,面板日志会记录失败的请求
- 点击日志条目查看,如果
消息头/Headers中没有响应头/Response Header这个域名可能就是被 SNI 封锁 - (如已排除 DNS 污染和 IP 封锁可跳过此步骤)Ping 上一步得到的域名,如果有回应,基本可以肯定就是 SNI 封锁,否则就是 DNS 污染或 IP 封锁
- 可以先尝试不发送 SNI(配置方法见上方),服务器将发送默认证书,如果证书匹配则连接成功
- 如果上一步失败,尝试限定使用 IPv6 连接(如果有且未被封锁)
- 如果上一步失败,尝试修改原域名的次级域名作为 SNI,如:
linux.do = connect.linux.do - 或者修改成同一个服务器的其它未封锁域名,如:
linux.do = linuxdo.org - 也可以尝试任意其它你想要使用的 SNI 名称
- 如果被封锁网站与其它网站共用 IP(如免费 CDN),则必须发送 SNI,且保留主域名不变
解决:前置技术
Mozilla 和 CloudFlare 主导了一项对 SNI 的改进方案,称为 Encrypted SNI (ESNI)。这个提案没多久被淘汰了(特征显眼,容易一刀封),取而代之的Encrypted ClientHello(ECH),浏览器端基本支持,网站/服务器端如google为什么不部署呢,可能是怕针对,俄罗斯去年就对Cloudflare ECH的站点进行了一刀封,看到就拦。总之“道高一尺 魔高一丈”
现在有一个较为通用的方法,域前置 Domain Fronting。它的原理简单来说是这样的:部分服务器允许 TLS 连接说自己需要域名 A,但之后的 HTTP 协议说自己需要域名 B。或者服务器压根就不看 SNI 的信息。在这种情况下,对于一个黑名单的域名, 我们在建立 TLS 的时候,可以选用一个不在黑名单的域名,绕过对 TLS 连接的监测。它的缺点是,依赖于服务器行为。也就是说,每个不同的站点,可能都需要不同的策略(域名)。
- L站的服务器实际上是不看SNI的。在建立TLS连接的时候,即使不携带SNI,也可以正常进行 HTTPS 访问。
steamcommunity.com是一个黑名单域名,但同ip的valvesoftware.comv社就不是。我们在建立TLS连接时,使用 SNI =valvesoftware.com,之后的 HTTP 请求依然可以正常连到steamcommunity.com。
sni伪造是利用了域前置技术实现的,通过修改 SNI 头的方式可有效抵御中间人攻击,不推荐
这里简单介绍一下,通过修改浏览器启动参数伪造sni(chromium内核,141.0.7356以下版本)绕过SNI阻断。这里用 discord.com 举例,linuxdo不行( 网站服务器有严格的 SNI 安全设置,无法解决,通常表现为证书无法验证,伪造 SNI 连接失败;还可能直接返回错误页面。)
# 在浏览器图标-属性-快捷方式-目标 空格后粘贴
--host-rules="MAP discord.com dis" --host-resolver-rules="MAP dis 162.159.128.233" --test-type --ignore-certificate-errors
[!danger]下面重点来了
最近利用域前置搞了一个玩具,通过把被屏蔽域名解析到本地(hosts/DNS),把本地作为 HTTPS 终端代理(使用内置 Nginx),然后用本地 CA 签发证书做 TLS 握手,Nginx再向目标 IP 发起上游连接并“改写/隐藏”上游的 SNI,从而避免中间审查设备识别真实 SNI 并阻断连接。
为什么能绕过sni阻断:
- 阻断器(GFW)的 DPI 通常会解析 TLS Client Hello 并读取 SNI 字段,如果它看到某个受限域就会阻断。让“外部网络”只看到 Nginx 向上游的 SNI(一个无害或允许的域,或根本没有发送真实域),而不是客户端原始的被封域名。
- DNS 被劫持到本地,客户端与远端的 TLS 握手在本地 Nginx 完成,外部只看到 Nginx 与上游服务器的连接(上游 SNI 可被重写)。这样“隐匿真实 SNI”,实现所谓 “域前置 / SNI 前置”
演示视频:

