体验中国的 GFW 全文翻译
2016.1.19 日本站介绍 “国外信息安全专家体验防火长城” 的全文中文翻译来了。原文:http://blog.zorinaq.com/?e=81@holly_lee (Blog)翻译中文:http://weibo.com/p/1001603945665913854240体验中国的 GFW最近我第一次去了中国。作为一个信息安全专业人士,终于能够亲手摸一下中国的 GFW, 看看它是如何工作的以及翻墙的难度如何,对此我充满了好奇。简短地说,我惊讶于如下方面:它的成熟度很高。比如能够利用 TLS 的侧信道泄漏 (我有关于它能够检测到 安全 web 代理中的 ”TLS 中的 TLS“ 特性的证据)用一些简单的 Unix 安全工具就能翻墙。在中国排名最前的 3 个商业 VPN 供应商里,有 2 个 使用了太短的 RSA 密钥 (1024 位)。中国政府能够把这么短的密钥分解因数出来。(2016-02-15 更新:在接到我的报告之后,这两个供应商停止了短密钥的使用,现在它们用 2048 或者 4096 位的了)为什么要翻墙?很多到中国的西方人有非常合理的理由需要翻墙。GFW 阻止了所有的 Google 服务。这意味着没有 Gmail 来访问你的电子机票,没有 Hangouts 来跟家庭联络,没有 Maps 来寻找旅馆,没有 Drive 来存取旅行计划文档。这就是我翻墙的主要需求。在去中国前我做了一些准备。在手机上我把 Drive 应用里的文档存下来以便离线访问。在 Maps 应用里我把我要去的地方的地图预先加载好,通过放大这些地点我把附近所有的街道和景点的地图都加载到手机上了 -那时候 Google 地图还没有新的离线功能。不过最终 Maps 还是几乎没什么用:我的 GPS 位置总是与真正的地点偏离几百米远。这是因为中国的 GPS 移位问题。(Google 完全可以在它的中国地图上使用 WGS-84 坐标来解决这个问题,可它为什么至今还没做呢?)想法 1就这样我到了北京的旅馆里,试图访问 google.com。访问出错了,GFW 发送了 TCP RST 包阻止了连接。我第一个想法是建立一个 SSH Socks tunnel (ssh -D),从我的笔记本连到美国一个数据中心的服务器上。我用了如下命令来配置 Chrome:$ google-chrome –proxy-server=socks://127.0.0.1:1080$ ssh -D 1080 my-server这种方法工作了几分钟。然后就开始出现严重的丢包,丢包率大概有 60% 到 70%。重启这个 tunnel 又能工作几分钟,然后丢包又出现了。这种丢包会影响到达服务器的所有流量,跟数据种类无关,无论是 SSH 连接还是简单的 ping 都一样。还不清楚 GFW 为什么丢弃数据包。有人说那是在不彻底阻断 VPN 的情况下,对 VPN 进行的干扰。也可能使 GFW 选择了一些可疑的数据包,将它们导向一个子系统来进行深度检测,而这个子系统过载了,没法处理全部流量。不管原因是什么,这样的掉包使得 SOCKS tunnel 变得又慢又不稳定,没法用。想法 2 我尝试了一个稍有点不同的方法:在服务器上运行一个 web 代理 (polipo),对 127.0.0.1:$port 进行侦听,然后用 SSH 端口重定向 (ssh -L) 去访问。$ google-chrome –proxy-server=127.0.0.1:1234$ ssh -L 1234:127.0.0.1:$port my-server一样地,这种方法正常工作了几分钟,然后又出现了丢包。GFW 能够清楚地检测和干扰哪些携带了大量数据的 SSH 通信。想法 3 可以用一个在 TLS 连接之上的代理来代替 SSH。这样 GFW 应该难以检测了,因为通过 TLS 连接来访问代理的流量模式跟访问一个 https 的站点是很接近的。我们把 TLS 连接之上的 web 代理称为安全 web 代理。大多数浏览器还不支持,所以它并不普遍。我用了 stunnel 将一个代理连接包装到 TLS 里,并为我的笔记本打开了一个未加密的代理端口。当然,我需要用身份认证来保护这个配置。不过我不能用标准的代理认证方法,因为如果 GFW 连到它上面,一个 “407 需要代理认证” 就会将它暴露。我也不想用 TLS 客户端认证,因为那样很可能会表示出这可能是某种基于 TLS的 VPN。我仍然要尽可能地让我的安全 web 代理看上去像,活动起来也像一个常规的 HTTPS 端点。我用 Python 写了个短小的中继脚本,它在 $port_a 上侦听并将所有的连接转发到另外一个端点 $host_b:$port_b. 这个中继可以运行在两种模式下。在 “客户端模式” 下(在我的笔记本上)它会将一个 128 位的密钥作为发送到连接的头 16 个字节。在 “服务器模式“ 下(运行在我的服务器上)它会对这个密钥进行校验,只有校验成功才会转发连接,否则就丢弃数据,像一个失去响应的 web 服务器一样。在我的笔记本上的配置如下:配置浏览器使用 127.0.0.1:5000 上的代理中继脚本在 127.0.0.1:5000 侦听, 插入密钥,并转发到 127.0.0.1:5001stunnel 客户端在 127.0.0.1:5001 上侦听, 将连接包装进 TLS,并转发到 my-server:5002服务器端:stunnel 服务端 my-server:5002 上侦听, 解出实际连接,并转发到127.0.0.1:5003中继脚本在 127.0.0.1:5003 上侦听, 校验密钥(并移除之),然后转发到 127.0.0.1:5004Web 代理在 127.0.0.1:5004 上侦听结果如何?工作得很好!没有包丢失,没有任何问题。在通过这个代理浏览 HTTP 站点时 GFW 会在线路上看到什么呢?在我的系统上对 “curl –head http://www.google.com” 进行抓包结果显示如下 (TLS 记录的大小显示在括号里):C: TCP SYN to proxyS: TCP SYN+ACK reply from proxyC: TCP ACKC: ClientHello (86 bytes)S: ServerHello, Certificate, ServerHelloDone (67+858+9 bytes)C: ClientKeyExchange, ChangeCipherSpec, encrypted Finished (267+6+53 bytes)S: NewSessionTicket, ChangeCipherSpec, encrypted Finished (207+6+53 bytes)C: encrypted ApplicationData #1 (37+197 bytes)S: encrypted ApplicationData #2 (37+693 bytes)(旁注:ApplicationData 记录分成了两部分,第一部分 37 字节,这是为应对 BEAST 而做的 1/n – 1 记录分割)这里有一个 TCP 握手,一个 TLS 握手,一个从客户端发送的,大约 200 字节的加密 ApplicationData 记录(HTTP 请求),以及一个从服务端发送的,大约 700 字节的加密 ApplicationData 记录 (HTTP 回应)。这个 TLS 交换和流量模式类似于一个未经代理的 HTTPS 连接,因此 GFW 无法检测到它是一种翻墙技术。不幸的是,一旦我开始通过我的代理浏览 HTTPS 站点,GFW 就能检测到了并应之以大量的丢包… 它是怎么检测到的呢?想法 4通过一个安全代理浏览 HTTPS 站点会有两层 TLS:外层 TLS 连接到代理,内层连接到站点。我推断 GFW 能够猜测出加密的 ApplicationData 里隐藏了一个代理 CONNECT 请求和另一个 TLS 握手。如下为通过代理运行 “curl –head https://www.google.com” 是的抓包结果:C: TCP SYN to proxyS: TCP SYN+ACK reply from proxyC: TCP ACKC: ClientHello (86 bytes)S: ServerHello, Certificate, ServerHelloDone (67+858+9 bytes)C: ClientKeyExchange, ChangeCipherSpec, encrypted Finished (267+6+53 bytes)S: NewSessionTicket, ChangeCipherSpec, encrypted Finished (207+6+53 bytes)C: encrypted ApplicationData #1 (37+197 bytes)S: encrypted ApplicationData #2 (37+69 bytes)C: encrypted ApplicationData #3 (37+325 bytes)S: encrypted ApplicationData #4 (37+3557 bytes)C: encrypted ApplicationData #5 (37+165 bytes)S: encrypted ApplicationData #6 (37+85 bytes)C: encrypted ApplicationData #7 (37+149 bytes)S: encrypted ApplicationData #8 (37+853 bytes)对 GFW 来说,这 8 个 ApplicationData 记录看上去像 keep-alive 连接上的 4 对 HTTP 请求和回应。不过,根据 的研究所显示的,TLS 的侧信道泄漏是可以被利用的,例如用来观察包大小。试着做一下,我们能看到它们的大小符合一个 CONNECT 请求和一个 TLS 握手交换的信息大小:C: encrypted ApplicationData #1 (37+197 bytes):”CONNECT www.google.com:443 HTTP/1.1rnHost:…
阅读更多