加密代理入门

本文将带你基本了解加密代理的一些基本信息。请注意,本文不是一篇教学或者 How-to Guide,也不是精确的论文综述。本文的目的是给有兴趣接触加密代理领域的人提供一个简单的了解渠道,介绍比较重要的基本概念,让他们在阅读真正的教学时不会感到过于困难。

让我们开始吧。

什么是墙?

要想解释什么是翻墙,得先解释什么是墙。而想要解释什么是墙,就要先对互联网的结构有一个基本的了解。想要了解互联网的基本结构,自然要从数据包说起了。

所有在互联网上交换的东西本质上都是数据,而为了高效率地交换这些数据,大家就把数据分成一个个小包裹,名为数据包。数据包上面有一个特殊的标签,说明了它要去哪,这个标签名为数据包头;而剩下的部分则全部是数据包携带的信息,名为数据包体。一个数据包能含有的信息量非常有限,因此在实际生活中,每次访问互联网,我们都要交换成千上万个数据包。

有了数据包,我们还需要有能够处理数据包的设备。我们手中的设备是浏览器,而身处运营商机房、为我们提供互联网接入的设备叫做网关,我们希望访问的东西(例如网站)是服务器。每当我们发起一次访问,浏览器会向服务器发送一些数据包,名为请求,服务器收到请求后向浏览器返回一些数据包,名为响应。而网关在这个过程中承担了幕后工作。它将浏览器发送的数据包寻找合适的路径运输到服务器,也将服务器发送的数据包寻找合适的路径运输回浏览器,这个过程名为路由

聪明的你应该也猜到了,用于路由的机器叫做路由器,而路由器常常也承担了网关的工作。路由的方式多种多样,可以是手机到基站之间的蜂窝网络连接,可以是家里路由器或电脑连接的网线,可以是省级运营商之间互联的光纤。而在整个运输过程中,数据包也将经过多个网关的接力路由。很多时候,数据包还可以采取多条不同的道路到目的地。但无论如何,总要有一系列网关来完成这个任务。

现在,我们有了数据包,有了处理数据包的设备,可是我们怎么能确定哪个数据包发往哪里,又怎么区分不同的服务器呢?这就要用到 IP 地址端口号了。把服务器想象成一栋大楼,这栋大楼具有一个地址,标识了自己所在的位置,这样大家就能找到它。在现实生活中,大楼的地址和市政有关系;而在抽象的服务器中,它的地址就是 IP 地址,是由网关所分配的。大楼内部还有很多对外提供服务的窗口,这些窗口就是我们所说的端口。它们由服务器自己管理,如果服务器中有某些东西希望对外提供服务,就必须打开一个或几个端口,这样才能接受从外部传来的数据。

这就引发了一个有趣的问题:作为网关的设备可以看到浏览器和服务器之间所有的通讯,为了完成它的职责,它必须知道浏览器要和哪台服务器通讯,通讯的内容又是什么。这也给了它很大的权力——如果它 不希望一个服务器被人访问,就可以将这个服务器的 IP 地址或服务器的某个端口号加入黑名单,不处理(丢弃)任何发往这个服务器的数据包。如果我们设计一个设备或者编写一段程序来完成这个工作呢?如果把这种设备或程序和网关结合在一起,我们会得到什么呢?

恭喜你,你发明了防火墙。将这种体系扩大到全中国,部署在每一个 省级 运营商和海底光缆的入口处,你就发明了“墙”。或者用更技术话的语言说,“防火长城”,the Great Fire Wall,GFW。防火墙是它的本质,长城形容它独一无二的规模。当我们后面谈起 GFW 的时候,就需要意识到:GFW 不是某个独立存在的设备,它是依附在中国省级及以上层次运营商的网关上的一个附加物。

哦,对了:GFW 的相关技术是国家机密;它没有部署在你家的路由器中哦。

从 GFW 的角度来讲,想要封禁某个服务(例如 Google),最好的办法就是把 Google 旗下所有的服务器都封禁,也就是对要封禁的服务器建立一个黑名单。而 GFW 要封禁服务器也有好几种不同的手段,上面提到的封禁 IP 地址和端口号的手段只是其中之一。下面将介绍其他常用的手段。

首先是早期 GFW 常用的 DNS 污染,这是一种故意破坏互联网 DNS 服务的伎俩。想要理解 DNS 污染,就要先理解什么是 DNS。那么什么是 DNS 呢?答案很简单:域名是给人类读的,但机器并不认识域名,只认识 IP 地址。需要有一个系统管理域名和 IP 的对应关系,将域名转换为 IP 地址,这个系统就是 DNS 系统。

DNS 系统本来应该是一个利好大家的系统,因为互联网上有无数的域名,又有无数的 IP 地址,管理它们之间的对应关系着实是一件困难的事情。但 GFW 为了进行破坏,故意污染了 DNS 系统:如果它希望封禁一个域名,就会故意让 DNS 系统中这个域名和 IP 之间的对应关系错乱。这样,任何在 DNS 中查询这个域名的浏览器都无法前往正确的地址,造成连接中断。

还有一种手段叫做 SNI 阻断。刚刚提到数据包的时候,我们有谈到数据包的包头含有关于数据包发往哪里的信息。但如果有很多域名都指向同一个 IP 地址,这个地址上的服务器就会产生混乱,不知道数据包到底是从哪个域名那里来的。为了解决这个问题,人们不得不在数据包的包头中加入 SNI,用于帮助服务器判断这个数据包是在哪个域名的指示下来到服务器这里的。但 GFW 利用了这一点,一旦 SNI 中出现了黑名单中的域名,GFW 就会丢弃对应的数据包,阻断连接。

但无论敌人多么强大,我们的任务都没有改变:找到一种方法,让浏览器能够访问 GFW 黑名单中的服务器,而 GFW 不知道浏览器实际访问的是谁。这也是翻墙的本质。

注:我们此时将所有访问互联网的服务抽象为浏览器,包括真正的浏览器,或者抖音等 APP(因为它们的本质和浏览器一样)。少部分例外会在后面提到。

怎样才能“翻墙”?

说起来很容易,做起来却很难。让我们考虑一个简单的场景,我们的目标是访问 Google,这样 Google 的服务器就是我们的目标服务器。如果浏览器直接和目标服务器通讯(即使目标服务器在 GFW 的黑名单里面),就必须向 GFW 告知目标服务器的身份。而一旦你告诉 GFW,它就会破坏你的连接。因此,浏览器绝对不能直接和目标服务器通讯,因为无论采用何种方法,只要 GFW 还处理两者之间的连接,就一定有能力进行破坏。

那么,怎么办呢?

幸运的是世界不仅只有中国大陆一个地区。除去已经部署了 GFW 的中国大陆,还有从中国进口了相关技术的伊朗、俄罗斯以及巴基斯坦,世界上还有很多地方。所以,只要移民到这些地方就可以了。这被称作“肉身翻墙”,是所有手段中最有效、最稳定的一种。当然,除此之外也有别的办法,其核心思想和肉身翻墙也差不多。

让我们用炒股来举个例子:对于一个想要进入美股市场的人来说,他在国内很难合理合法地获得美国银行账户来炒股;但如果他有一个在美国留学的朋友,就可以请求他的朋友办理一个银行账户,然后自己用来炒股。也就是说,假如我们先把自己想要请求什么的 意愿 送出国,然后在国外 实现 这个请求,是不是就实现了绕过 GFW 的目的呢?

这听起来比肉身翻墙容易多了。事实上,有一种专门的工具用来实现这个目的——这就是代理服务器。一般情况下,如果浏览器决定访问一个目标服务器,它会直接向目标服务器发送数据包;但如果浏览器 被用户要求 使用代理服务器来访问目标服务器,它会将数据包发送给代理服务器,然后由代理服务器向目标服务器转发这些数据包。这就是代理的本质,即“代为处理”。

这样,在目标服务器看来,是代理服务器(而不是浏览器)向自己发起了请求;而在 GFW 看来,浏览器是在和代理服务器(而不是黑名单内的服务器)进行通讯,因此不会进行破坏。由于 GFW 使用黑名单制度,我们只需使用一个不在黑名单内的国外服务器,就可以轻松地创建一个代理服务器了。

有这样一个代理服务器固然是好的,但 GFW 很快就发现了这个问题,因此它会想办法阻断这些代理服务器。一旦 GFW 确定了某个境外服务器是代理服务器,那么就会毫不留情地把这个代理服务器放入自己的黑名单。这样,代理服务器就遭受了和目标服务器一样的待遇,它和客户端之间的通讯也被破坏了。因此,我们必须想出一招,让 GFW 无法确定 哪些服务器是代理服务器。这种专门设计为隐藏信息、不暴露自己存在的代理,我们称之为加密代理。负责执行加密代理的代理服务器就是加密代理服务器,而浏览器和代理服务器之间通讯方式的约定,我们称之为加密代理协议

在后面的文章中,我们将不再使用不严谨的“翻墙”一词。“翻墙”这一行为的本质是使用加密代理访问目标服务器,“翻墙”的动作实质上是指示浏览器使用代理服务器进行通讯,“翻墙”能否成功取决于加密代理协议的设计是否足够强大以至于不会被 GFW 发现。通过使用“代理”、“加密代理”或“加密代理协议”来取代“翻墙”,我们能够进行更严谨的讨论。而且,我们也会减少使用浏览器和服务器这种笼统的说法,而是使用客户端服务端的说法。因为一个服务器上可以运行多个加密代理服务端,一个浏览器也可以借助多个加密代理客户端发出请求。

概念引入:

  • 协议:协议是一种纸面上的规范,它就像是一张设计图,或者一门语言,规定了浏览器和服务器之间通讯的语法和规则。加密代理协议事先约定了如何进行加密(或者解密),这样浏览器和代理服务器之间才能通过加密代理进行通讯。但需要指出的是,协议本身只是一张纸(或者一个文档),没有执行任何操作的能力,必须配合对应的实现来使用。

  • 实现:如果我们编写一个软件,专门按照协议的规定来进行操作,这个软件就是协议的“实现”。例如,我们先学习英语,然后我们用英语进行对话,在这个过程中,英语本身就是协议,而使用英语对话的我们二人就是协议的实现。在加密代理领域,实现一般不区分客户端和服务端——这很容易理解,因为如果你能听懂英语,那么你大概率也应该(并且需要)说英语。软件也一样,同一个实现既可以当作服务端,也可以当作客户端,只要经过恰当的配置即可。

  • 服务端和客户端:加密代理协议是为了一个小众的需求(绕过 GFW)引入的,因此浏览器或者其它应用程序不愿意为加密代理协议创造一个实现。当然,协议的设计者自己会创造一个实现。对于代理客户端,浏览器和它安装在同一台电脑上(因此 GFW 无法介入),它们之间使用标准的普通非加密代理协议进行通讯;代理客户端和代理服务端之间使用专门的加密代理协议进行通讯,这样可以欺骗 GFW;代理服务端和目标服务器之间则公开进行通讯,因为此时已经没有 GFW 进行破坏了。

注:加密代理的本意指的是把发送给代理的数据加密,让外部观察者不知道浏览器和代理之间的通讯内容。但在这篇文章的语境中,我们提及的“加密代理”都是专门为了绕过 GFW 而设计的。这是因为隐藏通讯内容是隐藏代理服务器存在(欺骗 GFW)的必要不充分条件。所以,我们对加密代理的定义要比一般情况下更严苛。这种严苛并不是没有代价的,专门为了绕过 GFW 设计的加密代理通常都存在着性能或易用性上的妥协。

加密技术大讨论

讨论加密代理之前,有必要对现在经常使用的加密与密码学技术进行简要的说明。现代密码学是一门严肃专业的学科,其能力与应用范围经常超出人们的朴素想象。由于其与数学的联系过于紧密,因此下面的说明不仅不严谨,而且也不会解释加密的原理。我们更着重于加密能做到什么

加密:什么是加密?

当我们提到加密时,人们很容易产生一种错误的印象,也就是用某些东西来 保护 自己的数据,就好像把珍贵的物品放入保险箱一样。毕竟,加密需要一个加密算法,以及一个密钥。加密算法负责处理数据,而密钥则是解密的关键——听起来就很像一个保险箱和它的钥匙,对吧?

但实际上,这并不是真的。没有任何东西保护了你的数据;加密算法不保护任何数据,它只是彻底改变了数据的存在形式。当我们说原始数据被“加密保存”时,原始数据其实是变身了:在被加密的那一刻,原始数据就已经不复存在,整个可观测宇宙中的任何地方都再也找不到它的痕迹。相反,保存下来的东西叫做密文,但它既不是原始数据,也不包含任何来自原始数据的片段。换句话说,它和原始数据之间完全不同

但是,这并不是说密文和原始数据之间没有关系。相反,密文包含了有关原始数据的所有信息,这些信息可以被用于精确地重建原始数据,这个过程被称作解密。我们使用名为密钥的东西,它记录了密文和原始数据之间的关联,人们可以从密文中重建原始数据。也就是说,密文包含了有关原始数据的全部信息,而密钥告诉人们怎么解读这些信息。最终,在密钥的帮助下,人们从密文中复原出原始数据本来的模样。

如果你还记得一点点数学,你可以深入加密算法的本质:它是一个数学函数;它接受两个变量,一个变量是要加密的数据本身,另一个变量是加密的密钥。而函数的运算结果则是加密的密文。如果我们只知道函数的结果而不知道第二个变量是什么,就永远不可能猜出第一个变量。而函数本身,也就是加密算法,是公开的,没有任何保密的必要。

我们非常有必要强调上述事实,并且必须使用强烈的词语来确保这一点会给我们留下深刻印象,因为这一点很反常识的同时又是现代密码学的基石。同时,它也揭露了现代加密的两个特性:其一是密钥一旦丢失,加密过的数据就再无法恢复;其二是如果想保证数据安全,只要确保攻击者拿不到密钥即可,密文可以随意传播。

编码:什么不是加密?

加密,正如我们所说,是为了让没有密钥的人无法解读信息。但加密过后的密文是无法解读的,这很容易带来一个错误印象——无法解读的东西都是密文。

但实际上,并不是这样。“看上去”无法解读的东西并不一定真的无法解读,可能只是相同的数据改变了一个表现形式而已,这种操作叫做编码。编码后的数据和原始数据几乎是等同的,任何人都可以解读编码之后的数据,只要使用合适的算法来解码就可以了。

编码的作用就在于它可以改换数据形式——既可以用于通过某些论坛或者平台的审查机制,也可以用来解决特殊字符的问题。但我们需要注意的是,不要把编码当作加密,编码不提供任何安全性。编码后的信息可以被任何人随意解读。

哈希:什么东西不可逆?

本小节讨论的东西有一个统一的英文名称:Hash。它目前通行的翻译是“哈希”,这是一个音译,也是目前来讲最常用的翻译;但严格来讲,哈希这个翻译是不容易理解的。Hash 还有一个翻译叫杂凑,一个翻译叫散列。为了帮助理解,在这一小段中我们会使用杂凑这个翻译。

我们上面讲的加密和编码都是可逆的。无论算法多么复杂,只要有对应的方法(如果是加密还需要密钥),就一定能得到原文。但杂凑和它们都不一样。当我们对原始数据进行杂凑时,事实上是从原始数据中提取某种特征,也就是从乱的原始数据中拼出一个摘要。相较于原始数据,摘要短得多,其中仅包含有关原始数据的特征信息,而大部分的原始数据信息在这个过程中都丢失了。这种丢失是不可逆的,没有任何人能从杂凑生成的摘要中恢复原始数据。但这个摘要和原始数据之间却存在着极大的关系:

  • 对于同一个原始数据,经过同样的方法杂凑后,总会生成相同的摘要;
  • 对于不同的原始数据,即使它们很相似,在同一个杂凑方法下生成的摘要也大不相同;
  • 对于不同的原始数据,无法人工让它们在同一个杂凑方法下生成相同的摘要。

杂凑的不可逆性,外加上述的几条特性,使其很适合当作数据的 指纹 使用。当然,这些是理想的杂凑方法才有的特性,现实中的杂凑方法并不全都这样完美,不过如何改进它们也不是我们应该思考的。

最后,我们还要回过头面对 Hash 这个名字的混乱。为了交流方便,我们做一些约定:

  • 哈希值代表上面所说的摘要的值
  • 哈希函数代表执行杂凑时所用的方法

请灵活对待。

对称加密

对称加密是最传统也最常见的加密方式。在对称加密过程中,我们使用一个密钥将一段数据加密,而同一个密钥还可以对数据进行解密。一切看起来都很简单,并且很符合直觉。在网络上(包括加密代理协议中),对称加密是使用最广泛的加密方式,客户端和服务端之间预先约定一个对称加密密钥,而所有数据包的包体都被对称加密密钥所加密。这样,即使攻击者截获了加密过的数据包,也不可能知道其中含有什么。听起来万无一失,事实上也万无一失——除了一个问题,那就是怎么预先约定一个对称加密密钥呢?

非对称加密(公钥加密)

为了解决这个“怎样约定对称加密密钥”的过程,人们发明了非对称加密。观察对称加密的过程,我们很容易发现,数据需要 先被加密,然后再被解密。但这个过程中我们只用到了一把密钥,这把密钥同时具有加密和解密的功能——如果我们有能力把它拆分成两把密钥呢?我们让其中一把只能用于加密,另一把可以用于解密。而且,用于加密的密钥无法反向推断出用于解密的密钥。这一对分别用于加密和解密的密钥被称作密钥对

不要思考我们怎么做到这一点(那需要三个数学博士学位),转而思考如果我们做到了这一点之后会有什么变化。虽然并不显然,但让我们设想一下:如果把用于加密的那把密钥公开,那么任何拿到加密密钥的人都能加密自己的数据;但只有拥有解密密钥的我们才能解开被加密的数据。既然我们的目标是在服务端和客户端之间约定一个对称加密密钥,那么我们就可以如下操作:

  1. 服务端生成一对密钥,把其中用于加密的那一把公开发布,用于解密的那一把保留在自己手中。
  2. 客户端收到公开的加密密钥,然后自己生成一个对称加密密钥。他将对称加密密钥复制成两份,第一份保留在自己手中,第二份使用公开的加密密钥变成密文。
  3. 客户端将密文发送给服务端,服务端收到后使用自己手里保留的解密密钥解开密文,获得其中的对称加密密钥。
  4. 现在客户端和服务端都拥有相同的对称加密密钥了。
  5. 攻击者也获得了公开的加密密钥,还有客户端发送出去的密文,但两者对他毫无用处,因为他不能用加密密钥来解密密文,也不能从加密密钥中推断出解密密钥。

感谢姓名首字母分别是 RSA 的三名科学家,我们做到了。也就是说,我们获得了一种方法让客户端和服务端之间约定一个秘密——一个对称加密密钥。后续我们会经常使用公钥私钥的称呼,公钥代表公开的密钥,私钥代表保密的密钥。这种加密所用密钥被公开的体系叫做公钥加密

非对称加密(数字签名)

严格来讲,数字签名是非对称加密的一种应用,而不是一种加密方式。数字签名也需要一个公钥和私钥,但它解决的是另一个问题:如何确保数据没有被篡改?现实中的签名由于笔迹原因很难模仿,因此签名的文件可以确保真实;这个思想也被用在密码学领域,带来了今天的数字签名。数字签名的基本原理大致是反向使用非对称加密,还要结合哈希函数。我们先生成用于加密的密钥和用于解密的密钥备用,接下来我们做下面的操作:

  1. 服务端准备需要签名的文件,计算文件的哈希值和两把密钥
  2. 服务端使用加密密钥对文件的哈希值加密,然后安全保存加密密钥
  3. 服务端将文件本身、文件的哈希值和解密密钥公开
  4. 接收到文件的客户端用相同的方法自己计算文件的哈希值,并且使用解密密钥解开服务端提供的文件哈希值
  5. 客户端对比两个哈希值完全一致,确定这是来自服务端的文件
  6. 攻击者希望替换文件,但他没有服务端的加密密钥,无法对文件的哈希值加密
  7. 攻击者希望构造一个新文件,让新文件的哈希值和原始文件一致,但哈希函数的特性阻止了他这么做

由此,我们可以确保一个文件的内容不被篡改。这种情况下,我们使用的是公钥解密。值得注意的是,在这种情况下,用于解密的密钥不能推断出用于加密的密钥。对比一下上面的公钥加密模式,我们会很自然地发现:永远是公钥不能推断出私钥,但私钥其实可以推断出公钥。

挑战

不要误会!“挑战”指的不是加密方法受到了挑战,而是非对称加密,或者说密钥交换中的一个步骤。

学习过上面的部分后,聪明的你很可能已经发现了:对于两种非对称加密的应用来说,我们实现的都是“你知我知,但帮观者不知”的状态。我们可以把这种状况抽象为一个挑战挑战者生成一个随机数,然后将这个随机数使用密钥加密之后发送给被挑战者。如果被挑战者真的拥有解密的密钥,那么它就可以轻松解密出这个随机数,然后公开给挑战者,挑战者一看便知;而没有解密密钥的攻击者只能对着加密之后的随机数发呆。

我们还可以把这个挑战嵌套一层。挑战者生成的随机数实际上是对称加密密钥,被挑战者解密的也是对称加密密钥。但被挑战者并没有公开这个对称加密密钥,而是自己使用对称加密密钥加密了一个随机数,然后发回给挑战者。挑战者手中本来就持有这个对称加密密钥,因此它也可以很轻松地解密随机数。这样,挑战者就知道被挑战者已经拿到了对称加密密钥,完成了挑战;而被挑战者不仅成功完成了挑战,还和挑战者分享了一个“秘密”。

这种挑战是密钥交换过程中重要的一环,此时被挑战者证明了自己手中有解密密钥,也证明了自己拿到的密钥和挑战者生成的密钥一样,但没有向旁观者透露任何有关密钥的信息。非常有趣!

证书与 TLS

虽然上面的过程听起来都很完美,但别忘了我们面对的是国家级攻击者 GFW,它比我们想象的更邪恶也更强大。GFW 作为网关,可以很轻松地执行中间人攻击,方法如下:

  1. 当客户端发起请求时,GFW 假装服务端来响应客户端的消息。它将自己的公钥发给客户端。
  2. GFW 同时假装客户端来向服务端发起请求,获得了服务端的公钥。
  3. GFW 和客户端约定对称加密密钥,又和服务端约定对称加密密钥。
  4. 这样,客户端和服务端以为自己在互相通信,但 GFW 可以在中间窃听。不仅如此,由于有了加密密钥,因此它可以随意篡改两人之间的消息,让客户端和服务端永远无法察觉。

事实上,中间人攻击甚至不需要一个国家级的攻击者就能发动,即使是小型运营商甚至个人黑客也可以做到。而且非常显然,无论是对称加密还是非对称加密都无法阻止这样的攻击。

看上去我们的加密尝试失败了……吗?

好吧,某种意义上来说,是的,但从另一个角度来讲并没有,这就是我们接下来要介绍的 TLS

中间人攻击逼迫我们引入一个可信第三方的存在,建立起名为证书验证的体系,这个第三方就是证书颁发机构。而证书验证就是这个可信的第三方向大楼颁发证书:请看,这就是某某企业(域名)的大楼(服务器)。

证书颁发和验证的过程需要综合使用上述的各类密码学手段。证书颁发机构会使用公钥加密来证明自己的身份,为了避免证书颁发机构和服务器之间的通信被中间人影响,证书颁发机构的公钥从一开始(操作系统安装时)就内置在操作系统中。这样,中间人就不能截获证书颁发机构和服务器的通讯,因为它没有证书颁发机构的私钥。

证书颁发的流程还需要验证证书申请者对域名的所有权,但这是一个单独的部分,不涉及什么密码学知识。我们简单地假设证书颁发机构已经验证了申请者对域名的所有权,接下来是证书颁发的流程:

  1. 服务端向证书颁发机构请求颁发证书,它使用证书颁发机构的公钥来挑战证书颁发机构
  2. 证书颁发机构挑战成功,和服务端建立加密通讯
  3. 服务端自己生成一个公钥加密密钥对,将自己的域名和密钥对中的公钥合成一个文件,提交给证书颁发机构,私钥则安全保留
  4. 证书颁发机构验证域名的所有权
  5. 验证域名所有权后,证书颁发机构对含有域名和公钥的文件数字签名,此时这个经过签名的文件就变成了一张证书
  6. 服务端下载证书
  7. 当客户端访问服务端时,服务端出示证书
  8. 客户端通过证书颁发机构的公钥验证数字签名
  9. 公钥验证通过,客户端确认证书可信,公钥和域名与服务端相对应
  10. 客户端使用证书中的公钥向服务端发起挑战
  11. 服务端挑战成功,客户端和服务端建立加密通讯

其中,第 7-11 步的过程又被称作 TLS 握手。在这个过程中,客户端通过证书和一系列挑战验明了服务端的身份,避免中间人攻击;服务端通过公钥加密体系和客户端交换对称加密密钥;客户端用对称加密密钥加密后续的请求和数据包体,避免被第三方窃听。这就是 HTTP/TLS 技术,也是现代互联网安全的基石之一。

但证书验证和 TLS 本身并不是没有弱点的。它的弱点就是这个绝对可信的第三方:我们必须相信,这个第三方不会以任何方式泄露任何信息,即使受到政府部门的威胁。但在实际中,没有人敢于做出这样的保证——尤其是 GFW 也是一个国家级攻击者。好在,世界上有很多证书颁发机构,中国的 GFW 不太可能有能力威胁美国等国家的证书颁发机构来伪造一张证书。所以,我们还可以认为,证书验证体系目前是安全的。

加密代理的历史与现状

在了解了这么多前置知识之后,我们终于可以开始讨论加密代理本身了!俗话说:知己知彼,百战不殆。讨论现代的加密代理之前,有必要简单回顾一下加密代理的发展史。回顾历史的意义在于,GFW 和加密代理之间的攻防手段是互相进化的,只有了解曾经设计的弱点,才能明白为什么今天的加密代理协议需要各种各样的防御措施。我们将开发各类加密代理协议的社区称作反审查社区,因为社区的目标和理念是对抗互联网审查。

第一代加密代理:VPN

没错,这是一个历史遗留问题!

VPN 最初是为了让公司(或者学校)网络之外的人员能够访问网络内部资源的代理协议。VPN 代理使用对称加密,它确保其内部传输的信息不会被泄露,但是它没有任何伪装。这也就意味着 GFW 不知道客户端通过 VPN 和代理服务端说了什么,但它一眼就能确定客户端和服务端正在使用 VPN 协议通讯。因此,GFW 可以高效率封禁任何没有报备的境外 VPN 服务,但由于它出现的年代实在是太早了,尽管 VPN 早已不适合作为绕过 GFW 的加密代理,很多人还是使用 VPN 作为这一系列加密代理协议的统称(尽管这是错的)。

然而,VPN 却留下了一个方便的系统级别解决方案。由于企业经常有需求让员工的整台设备都通过 VPN 连接企业内网,因此操作系统总会留下一个 VPN 接口,在系统(而非浏览器)层面让 VPN 程序接管网络通信。这给代理客户端留了一个方便,它们可以借助操作系统的 VPN 接口强制让其它应用程序使用代理。

注:VPN 本身是一个协议簇,这意味着它包含很多不同的协议;而且各类 VPN 协议今天也在企业之间广泛使用,它只是不再能用于绕过 GFW 而已。

第二代加密代理:Shadowsocks

ShadowSocks,或称 SS,是一种全随机数代理。使用时,客户端和服务端需要提前约定对称加密密钥,然后客户端将数据加密之后发送给服务端,服务端再进行解密。这种代理有意思的地方在于,在 GFW 看来,客户端发送的东西就像是毫无意义的随机数据一样。GFW 不仅看不出客户端为什么要发送它,也看不出服务器为什么要接收它。这种全随机数的设计让 GFW 不能分辨两者之间到底是不是在使用加密代理协议进行沟通,因此可以欺骗 GFW 一段时间。

但是,GFW 发展出了主动探测重放攻击两个武器。主动探测指的是,对于那些有嫌疑的服务端,GFW 会亲自伪装成一个 SS 客户端和它发起连接。当然,GFW 不知道密钥,但 SS 服务端同样不知道对方是 GFW。因此,它会在后续的交流中暴露出独属于 SS 服务端的痕迹,这样 GFW 就发现了这是一个 SS 服务端,并且将其封禁。

重放攻击也是主动探测的一种,但它更加难以防御。虽然 GFW 不知道密钥是什么,但它可以记录客户端向服务端的通讯,然后在一段时间后将通讯亲自发送给服务端。这样,SS 服务端就可能把它当作合法的客户端连接,给出回复,暴露了自己是 SS 服务端的事实,进而被 GFW 封禁。

在这两种攻击的威胁下,SS 协议也被击败了。但它的问题可以通过工程手段加以改进,因此直到今天还有人在制作改进版本的 SS(名为 SSR)。最终杀死 SS 的是警方:他们通过某种手段找到了 SS 的作者,这也让 SS 最终遗憾退场。

第三代代理协议:vmess 协议

在 SS 式微后,一个名为 V2Ray 的社区建立起来,vmess 就是他们的成果。vmess 可以看作是 SS 的全面改进版,原理仍然是将数据伪装成随机的数据流让 GFW 看不透,但增添了不少对于主动攻击的防御手段。例如,vmess 强制要求时间验证,这样一旦 GFW 使用重放攻击,vmess 服务端就会识别出时间异常,进而拒绝连接。如果检测到密钥错误,vmess 会直接拒绝连接,就像是普通的服务器一样,让 GFW 不能通过主动攻击来探测服务端的存在。最后,vmess 还支持各种伪装:它可以让加密代理协议的外观看上去像语音通话、网页浏览之类的正常流量,进而减少被 GFW 怀疑的可能性。

在这样严密的设计之下,GFW 对 vmess 的识别能力大大下降了。然而,GFW 并没有放弃围追堵截:它们引入了先进的机器学习深度包检测技术。这种全随机数的协议具有很强的统计学特征:虽然单个数据包看上去是随机的,但综合识别大量的数据包,GFW 可以推测出客户端正在访问一个网站,进而判断和客户端通信的是代理服务器,然后执行封禁。这也宣告了今后的全随机数协议死刑。

第四代代理协议:TLS 时代

在 2015 年左右,浏览器厂商和互联网工作小组联合起来,推进了 TLS 的平民化和普及。事实上,TLS 的出现本身促进了 GFW 的早期升级——在大公司和组织也没有使用 TLS 的时候,GFW 可以实时识别网页中是否有违禁的词语,如果有才会加以阻断。而千禧年后 TLS 在大公司和组织(例如维基百科)中的普及让 GFW 无法这么做,因此它们只能直接屏蔽整个网站。在 2015 年之前,这项技术很昂贵,只有公司才能负担得起,个人用户是很少使用 TLS 的。但 2015 年后,免费的 TLS 证书推广开来,使用 TLS 技术的网站和流量大大增加。

这让社区看到了一丝希望:TLS 连接在建立之后,传输的内容也是完全加密的;虽然 GFW 知道服务端和客户端在进行 TLS 通信,但通信的内容却完全保密;最重要的是,尽管机器学习知道客户端在访问一个网站,但 TLS 本来就是为了访问网站而设计的,因此没有任何问题。如果通过 TLS 加密过的内容来传输加密代理呢?

在这个理念的指导之下,名为 Trojan 的代理协议诞生了。Trojan 将代理的请求使用 TLS 协议进行加密,这样在 GFW 看来,客户端和服务端之间的通信就是正常的 TLS 协议。这种藏木于林的思维大大减小了代理服务器被察觉到的可能性。然而,这却为主动探测带来了新的攻击面:TLS 协议基本上只用于网站,如果 GFW 访问有嫌疑的服务端,而服务端却并没有实际运行一个网站,那么就很容易暴露服务端。

为了解决这个问题,Trojan 协议必须引入名为回落(Fallback)的技术:服务器在运行代理服务端时,还要再运行一个网站;代理服务端实时检测入站流量是否合法,如果不合法(也就是来自 GFW 的探测流量),就将其回落到后端伪装的实际网站上。这引入了额外的复杂度,而且由于 TLS 的原理,想要设置这样的代理服务器必须拥有一个域名,这同样加大了持有成本。

同时,这一技术并不是万无一失的。常规的网站也使用 TLS 加密,因此这一技术在代理常规网站时相当于进行了两次 TLS 加密,引入了 TLS in TLS 的特征:回看 TLS 的握手阶段,我们很容易发现,每进行一次 TLS 握手,服务端和客户端就要进行一次挑战;如果客户端先和代理服务端握手,再和目标服务器握手,就要进行两次挑战,往来次数也增加到四次。这被称作 TLS in TLS 的生死四包,不仅极不寻常,而且很容易被 GFW 识别。

在 GFW 完全掌握了 TLS in TLS 的特征之后,社区不得不暂时放弃使用标准 TLS 来掩护加密代理的想法。

第五代代理协议:Xray-core

V2ray 社区在 vmess 失败后慢慢不再活动,但他们的技术遗产被用在一个名为 XTLS、专注于解决 TLS 伪装问题的项目中。这个项目开发了先进的流控技术,主要解决的是 TLS in TLS 的特征问题:由于 TLS 加密可以很好的保护数据包体,GFW 根本无法区分数据包体是来自代理服务端伪装的网站,还是我们透过代理连接的目标网站。因此代理服务端和客户端协同工作,一旦客户端成功和目标网站建立 TLS 连接,代理服务端就不再进行 TLS 加密,直接使用网站和客户端之间的 TLS 加密。这消除了 TLS in TLS 的特征并提高了性能。

此外,由于 TLS 本身对内层数据已经有了很好的保护,能够抵抗重放攻击和密码破解,因此对代理内部的数据进行加密已经不再必要。XTLS 项目使用了名为 VLESS 的轻量化协议,舍弃了协议本身的加密,完全信任 TLS 的安全性。但是,采用了这些策略也不能称得上是万无一失,因为在 TLS in TLS 特征消除后,GFW 还找到了通过 uTLS 指纹进行识别的方法。

一般来说,TLS 通讯通常由真实的浏览器发起,但加密代理实现程序一般使用 Go 编程语言开发,Go 编程语言的 TLS 通讯特征与真实的浏览器不同。开发者需要使用 uTLS 库伪装浏览器的行为,而 uTLS 库的伪装并不是万无一失的。一旦 GFW 发现了 uTLS 库的某些指纹特征,就可以借助指纹特征大批量识别代理流量并封禁,而反审查社区必须等待 uTLS 库更新,并同时更新加密代理实现程序。

最后,无论是流控上的改进还是 VLESS 协议的研发都没有缓解 TLS 伪装的复杂度和成本问题:用户必须手动搭建和配置回落以防 GFW 的主动探测,并且自行购买域名。基于上述种种缺点,开发者仍然必须对协议积极改进。

当代代理协议:偷窃的艺术

终于,是时候谈谈现在使用的加密代理协议了!

为了彻底解决 TLS 的复杂度和成本问题,开发者深入审视 TLS 加密的原理,提出了一个大胆的理念。在使用代理服务器时,客户端和服务端之间并不需要建立真正的 TLS 连接。不仅如此,由于代理客户端和服务端都在我们的控制下,因此两者也不需要公开交换秘密,可以直接提前约定对称加密密钥——换句话说,代理客户端和服务端之间只要“做做样子”就可以了。

那么,怎么“做做样子”呢?回顾 TLS 握手的过程,我们发现挑战成功的条件很简单,只要客户端和服务端都觉得成功就算是成功了;至于两者到底有没有成功,第三方是不可能知道的。因此,名为 Reality 的新一代代理协议使用了这样的办法:

  1. 服务端预先生成一个公钥加密密钥对
  2. 客户端预先保存这个公钥
  3. 服务端从另一个合法的网站中“偷”一个证书回来
  4. 客户端装作普通浏览器去访问服务端,但发送信息时却使用预先保存的公钥在数据包中加密保存自己的身份信息,以及一个对称加密密钥
  5. 服务端利用私钥解密客户端加密的内容,验明客户端身份,获得对称加密密钥
  6. 服务端将偷来的证书发送给客户端,但证书中属于被偷网站的公钥此时已经换成了服务端自己的公钥
  7. 客户端接到证书和服务端公钥,确认服务端身份
  8. 两者心照不宣地伪装 TLS 握手完成,两者使用先前提到的流控算法进行通信,消除 TLS in TLS 特征,实现安全代理

当 GFW 尝试主动探测时,就会发生另外一些有趣的事情:

  1. GFW 装作普通浏览器去访问服务端
  2. 服务端出示自己偷来的证书,假装自己就是证书上所写的服务器
  3. GFW 验证证书,但由于它不知道服务端和客户端预先约定的密钥,因此只能用证书上所写的密钥向服务端发起挑战
  4. 服务端发现客户端正在使用证书上所写的密钥进行挑战,确认这个客户端不是代理客户端
  5. 服务端直接将客户端的所有请求和挑战原封不动地传递给自己“偷”证书的服务器
  6. 被偷证书的服务器浑然不觉,和 GFW 的主动探测客户端正常握手和挑战
  7. GFW 收到了合法服务器的合法回复,结束了主动探测

这样,利用这种巧妙的流程,Reality 协议成功解决了 TLS 伪装的不少问题。首先,由于服务端完全不需要处理 TLS 握手相关的内容,所有证书都来自于被偷网站,因此在服务端侧消除了 uTLS 指纹问题;其次,我们不再需要自行购买域名,不需要架设网站来伪装,也不需要自己处理证书签发和续期,极大减少了成本和复杂度。

当然,Reality 协议也并非万无一失。首先,加密代理客户端侧仍然使用 uTLS 库来模拟浏览器的握手行为,因此仍然会残留 uTLS 的指纹特征;其次,如果有自定义域名指向 Reality 协议的服务端,通过自定义域名访问时会触发证书错误,因为 Reality 从其它网站偷取的证书不能被用在自定义域名中。不过,纵然有这些缺点,但 Reality 仍然在性能、可靠性和易用性等方面之间取得了良好的平衡。此外,它也在积极受到社区和开发者维护,因此通过 Reality 确保传输层安全性是目前使用较广、可靠性较高的一种代理方式。

展望未来:鹦鹉已死?

未来的代理协议应该是什么样子,或许谁也说不清楚。事实上,GFW 还有很多武器(我们会后面提到)可以使用,而反审查社区也有很多武器等待推广。可以肯定的是,这将是一场旷日持久的战争。未来的加密代理协议将会更加复杂、更加深入底层,但有一个原则是我们必须现在就认识到的——那就是鹦鹉已死

所谓的鹦鹉已死,指的是不能依靠模仿已有的互联网协议来实现加密。因为互联网时刻在变化,此时此刻我们的模仿看上去万无一失,但一旦下一刻环境发生了变化,模仿就会立即失效,原本完美的伪装也变成了可供追踪的特征。相反,我们要做的将是“成为”已有的互联网协议本身。

回看加密代理协议的发展历程,尤其是 TLS 部分的发展,我们不难发现:从代理服务器自己处理 TLS,到偷窃其它服务器的证书来让原始的 TLS 协议代替我们主动应对 GFW,胜者永远是更能利用合法环境的那一个,而不是模仿现有协议的那一个。这也为我们选择今后可能出现的各种加密代理协议提供了一条指导:选择那些善于利用已有合法协议完成目的的加密代理协议,而不是选择那些尝试自行实现或伪装合法协议的加密代理协议。

下面简单介绍几种目前已有的加密代理协议,它们虽然没有像 Reality 一样广泛应用,但也有各自的优点和可取之处。

NaïveProxy

该加密代理协议也是一个 TLS 伪装协议,它主要解决了 uTLS 指纹的问题。NaïveProxy 的客户端网络连接代码取自真实的 Chrome 浏览器,这彻底消除了任何 uTLS 库可能暴露出的特征。然而,和其它 TLS 伪装协议一样,NaïveProxy 也需要自行购买域名,并在加密代理服务端运行的服务器上维护一个网站。

ShadowTLS

这个加密代理协议的原理和 Reality 有异曲同工之妙,它也需要一个第三方网站帮助自己伪装。当接到代理客户端发来的请求后,ShadowTLS 服务端会将请求原封不动地转发给第三方网站,让第三方网站和代理客户端开始真正的 TLS 握手。但一旦握手完成,客户端和服务端之间就会偷天换日,开始发送代理数据包,而第三方网站和客户端之间的 TLS 连接则被弃之不管。

Hysteria

这是一个独特的加密代理协议,它把底层传输协议TCP 协议更换到了 UDP 协议,因为 UDP 协议的发送、接受和重试都更快一些。由于这一特性,很多 GFW 用于检测基于 TCP 协议的特征都失效了。但 Hysteria 协议具有独有的缺点(见下文),这限制了它的应用。

工程实现

众所周知,工程是在约束条件下完成任务的科学。

在我们已经有了 Reality 这样一个优秀的协议的情况下,还有什么东西能够限制我们呢?让我们从简单到困难来依次讲解吧。

怎么让软件“走代理”?

这个问题其实在上面已经有了一点点解答。也就是在本地使用 VPN,强制代理客户端接管。但除去 VPN 之外,还有很多优雅且实用的方式让软件通过加密代理来发送流量。

零、有些东西不该走代理

这里的“有些东西”很明确,就是ICMP 控制包和游戏等即时性高的应用。你可能不知道什么是 ICMP 控制包,生活中最常见的 ICMP 控制包就是 ping 命令。ping 命令发送的数据包和日常浏览时发送的数据包大不相同,因此你不应该也不能通过 ping 命令来测试代理是否配置对了。正确的测试方法是在终端中声明环境变量(见下文)后使用 curl 进行测试。

对于游戏场景,并不是不能走代理,只是走代理可能会劣化你的使用体验。很多时候代理会引入额外的延迟,而且游戏使用的 UDP 协议有可能不被你的加密代理协议所支持。最后,游戏厂商对机房 IP 更为敏感(见下文),因此你可能会遇到不必要的麻烦。

一、PC 情景

PC 是自由的。这意味着你对上面的软件大部分时候拥有很高的控制权。在 PC 上,软件一般来说可以用两种方式来使用加密代理:使用本地标准代理,或者 TUN 模式

1. 本地标准代理

这里的标准代理指的是 HTTP 代理SOCKS5 代理。这两种代理由相关国际标准规定,是绝大部分需要访问网络的软件应当支持的。在 PC 上面设置它们非常简单,对于代理客户端,一般来讲都会提供一个仅对本地或者局域网开放的 HTTP 和 SOCKS5 代理,所有进入这个代理端口的流量和数据包会被经由加密代理协议发送至服务端。这实现了加密代理协议与普通应用程序之间的分离:无论换用哪种代理协议或客户端,只要暴露出相同的标准代理接口,就可以让应用程序无缝使用。

对于有图形化界面的应用,可以找找看应用中网络相关的部分是否有“使用代理”选项,将本地代理客户端提供的 HTTP 或 SOCKS5 代理地址填入其中即可。而对于没有图形化界面的命令行应用程序,一般可以在终端中声明 HTTP_PROXYHTTPS_PROXYALL_PROXY 环境变量让其使用代理。环境变量一般以这种格式书写:

#这是一个 POSIX 风格的写法
#在 Windows 上,我们推荐有需求使用命令行工具的人自行学习 PWSH
#或者安装 git bash,这样你可以使用这种 POSIX 风格的写法
export ALL_PROXY='socks5h://127.0.0.1:10808' 
#注意:socks5h 是一个 curl 风味写法,而非标准写法。
#它代表强制在代理服务端进行 DNS 解析,以绕过 GFW 可能的 DNS 污染。
#但有些应用程序(尤其是一些 Python 库)不支持这种写法。
#此时你可以换用 HTTP 代理方法:
export ALL_PROXY='http://127.0.0.1:10809'
2. TUN 模式

TUN 模式指的是一种特殊的模式,和 VPN 类似,不同的是其所处的位置比 VPN 更加底层。在 TUN 模式运行时,代理客户端会模拟一个虚拟网卡,让操作系统将所有的网络通信交由代理客户端负责。这种模式非常强大,可以强行接管系统中所有的网络流量,那些拒绝使用代理的“刁民”应用程序此时也会通过代理进行通信。但同时 TUN 模式也存在着风险:如果配置不甚,就会造成全系统断网。

3. 命令行和图形化客户端

想必聪明的你已经发现了,加密代理服务端经常以纯命令行守护进程(无头)的形式出现。而我们也知道,加密代理客户端和服务端的程序一般并没有区别,只是通过不同的配置文件让它们做不同的工作而已。因此,我们也可以而自然而然地得出:我们也可以部署一个纯守护进程的代理客户端。

但是,代理客户端和服务端之间有不同的需求。对于灵活控制什么东西走代理、实时切换是否代理等需求,有一个图形化界面显然要方便得多。另外,如果你有必要使用 TUN 模式,那么同时也很有必要考虑使用图形化界面,因为这可以让你方便地切换是否使用虚拟网卡。

另一方面,将代理客户端配置为纯守护进程的好处也是存在的。首先,它可以享受更好的自动重启和随系统启动;其次,没有图形化客户端让它轻量化很多;最后,一些图形化客户端作者会在其中加入自己的 广告。如果你不太希望看广告(坦白讲,我觉得看这些作者推荐的机场广告也无可厚非,反正我们都自建了),也可以通过使用纯守护进程客户端来彻底避免任何广告。

二、手机情景

相较于电脑,手机是一种封闭的多得多的设备。这意味着你在设置代理时的选择权和灵活性要少很多。而且,手机还会遇到一个独有的问题:

1. 设备信任

坦白来讲,很多中国 OEM 的信誉令人怀疑,尤其是华为。有了内置国家反诈中心且不可关闭等前车之鉴,你很难相信它们不会向某些上级报告你手机中存在的代理信息。轻松一点考虑,你的代理服务器可能会因此遭到封禁;严重一点考虑,你本人可能会因此遭到不必要的麻烦。如果你信任你的设备,那么可以将加密代理客户端放置在手机上;但如果你不信任你的设备,考虑一下使用变通方案

2. 局域网代理

如第一章节所述,GFW 没有安装在你的家庭路由器上!所以,你可以在电脑上向局域网开放一个 HTTP 代理,这样当你的手机和电脑连接到同一个局域网(比如说你家里的局域网)时,你就可以在手机上使用电脑上配置好的代理了!试试看长按你的 WLAN 连接,选择“修改网络”,然后在“代理服务器”一栏填入电脑的 IP 地址,以及电脑上 HTTP 代理使用的端口即可。这样,你手机上的浏览器,还有那些守规矩的应用都会乖乖走代理的。

3. 手机上的加密代理客户端

如果你信任你的设备,认为这么做无可厚非,那当然可以在手机上安装加密代理客户端。它们会创建一个 VPN 连接来接管你手机的大部分网络通信,这样所有的应用的网络流量都会交由加密代理客户端管理——你可以在加密代理客户端中单独配置哪些应用走代理。

国际互联网出口

这又是一个令人头痛的问题。中国不是国际互联网的发源地,加入国际互联网的时间也比较晚,却有着全世界最多的网民——这造成了一个麻烦,那就是中国的国际互联网连接带宽不足。在晚高峰时段,也就是从晚上六点左右一直到第二天凌晨的时间里,从中国大陆连接美国等国家的普通服务器将会遇到严重的丢包、抖动和延迟问题。

需要指出的是,这些问题其实不能“归功”于 GFW,而是中国互联网出口真实存在的带宽短缺。当这种情况发生时,普通用户可以简单地不访问那些拥堵的服务器,但对于我们这些使用加密代理的用户来说,我们所有的加密互联网流量都需要通过加密代理服务端——这让我们对晚高峰和出口带宽拥堵的容忍度变得非常低。

当然,我们也有几种解决方案,请看下文所述。

1. 选用“精品”线路

对于各大运营商来说,他们把拥塞当成了赚钱的机会。中国大陆连接国际互联网主要的渠道是 163 骨干网,而中国移动、联通和电信在骨干网之外还各自独立运营着所谓的“精品线路”,分别是电信 CN2 GIA联通 9929移动 CMIN2 网络。这些“精品线路”的价格是 163 骨干网的数倍,因此用户也较少,导致拥塞问题较少发生。想要保证好的体验,可以在选购时关注一下服务提供商是否提供接入了上述几种线路的服务器。

此外,由于香港是中国的一部分但 没有墙,因此香港和中国之间的线路要比不稳定的国际互联网线路快得多。所以,如果你选择在香港购买服务器,你也可以在晚高峰时段获得稳定的体验。当然,无论是精品线路还是香港服务器,它们的价格都极其昂贵,甚至时常限量销售。如果你的预算吃紧,最好还是考虑别的方法。

2. 启用 BBR

BBR 是谷歌团队开发的一种新型拥塞控制算法,这种拥塞控制算法是为了解决长肥管道问题。我们不太需要了解拥塞控制的基本原理,但我们可以对 BBR 的好处做一个简单的说明。在传统的拥塞控制算法中,如果丢包率,也就是发送的数据包中丢失的比例升高,拥塞控制算法就认为线路发生了拥堵,开始降低发包速率。

然而,跨国海底电缆的丢包率是比较高的,此外受到光速限制,传输延迟也很高。但这并不意味着线路带宽低,很多时候丢包的发生只是因为短暂的信号干扰等原因。传统的拥塞控制算法无法识别到底发生了什么,只会一味地降低发包速度,这导致了很多不必要的等待。BBR 的作用就是避免这种情况,在线路没有实际拥塞而偶发丢包的情况下确保传输速度。

但在晚高峰时段,线路事实上发生了拥堵,所以 BBR 算法发挥的作用不会有想象中那么大。无论如何,启用 BBR 利大于弊,所以你可以先启用 BBR 并试试看。启用它很简单,只需要几行简单的命令,你的云服务提供商也可能在安装镜像中替你预先启用了。所以亲自查找并试试看吧。

3. 多倍发包

还有一种不道德的方法来解决这个问题。它基于一个很简单的思想:如果我们发送一份数据包,数据包丢失的几率是 ,那么如果同时发送 份相同的数据包,全部丢失的几率就变成了 。显然 ,所以收到至少一份数据包的几率增大了。

客观上讲这种方法确实减少了拥塞 对我们自己 带来的影响。但它不仅没有解决拥塞的问题,反而加剧了网络的拥塞,让其它用户的体验变得更差。因此,这种方法相当不道德;如果所有用户都使用这种方法,那么大家的利益都会遭到损害。我们有必要强烈谴责这种“多倍发包”的解决方法,并且拒绝使用这种解决方案。

需要注意的是,有些人会无意(或者怀着邪恶的目的)混淆 BBR 和多倍发包,然而两者完全不同。BBR 是合理的拥塞控制算法,而多倍发包只会制造公地悲剧。

4. 换换协议

有些协议,例如 Hysteria2(参见上文),宣称自己对这种拥堵的情况有更强大的应对能力。当然,这种加速伴随着不少的代价,也就是被 QoS 的风险。QoS 这个词语本来的意思是 Quality of Service,即服务质量,但问题在于线路的带宽是不变的,那么要怎么保证所谓的“服务质量”呢?答案是丢包——把那些服务优先级不高的数据包全都丢掉。很不巧,UDP 协议的数据包就是最容易被 QoS 的那一批。所以,换用这种协议有可能会提升你的网络质量,但当然也可能反向劣化你的网络质量。

5. 咬紧牙关

你非得在晚高峰用代理吗?

IP 地址限制

GFW 并不是世界上唯一的防火墙,很多公司出于各种原因,也会限制一部分 IP 地址访问自己的服务——这叫做风控策略。和 GFW 不同的是,风控策略检测的是代理服务端的 IP 纯净度。

之所以会有风控策略,是因为一个事实:IP 地址可以粗略地分为家庭 IP机房 IP 两种。家庭 IP 指的就是个人或家庭使用的 IP,而机房 IP 指的就是服务器使用的 IP。对于以社交为主的网络平台,例如 Facebook,它们的用户大部分都在家庭中,因此大部分合法访问来自家庭 IP。反过来,使用服务器 IP 访问平台的人通常有着恶意的目的——要么是使用爬虫来爬取平台内容,要么是使用代理服务器绕过防火墙进行访问(很遗憾,这就是我们)。

因此,如果你的机房 IP 不够“纯净”,就很可能遭到各类平台的轮番封禁。更遗憾的是,这种“纯净”通常是不太可能在购买之前判断出来的。一般来讲,稍稍昂贵一点的服务提供商旗下的服务器 IP 会更纯净。你最好祈祷这个 IP(还有附近的 IP)之前的主人没有干过坏事!

对抗的艺术

无论协议看上去多好,我们都要时刻谨记:加密代理和 GFW 之间时刻都在动态对抗。GFW 永远会努力想出更邪恶的手段来破坏加密代理协议,而反审查社区也绝对不会坐以待毙。但我们自己却很容易因为短暂的安宁而放松警惕,长期不关注最新的社区动态和攻击结果,直到某次两会期间的集中封禁而失去和社区的联络。

因此,只要我们决定自行维护一个加密代理服务器,就要不时提醒自己阅读社区消息,跟上进展,了解最新一批遭到封禁的前辈为我们留下了哪些宝贵的经验教训。

GFW 的监视手段

本章节将会讲述面对目前而言比较实用的 Reality 协议时,GFW 还有哪些手段可以使用。尽管加密代理看上去是如此可靠,但邪恶而强大的 GFW 永远如芒在背。有些攻击方法(例如机器学习)是普通用户无法防御的,只能等待开发者修复;但还有一些监控方法,例如我们下面提到的,是可以通过恰当配置来避免的。让我们看看 GFW 有哪些手段,可以怎样威胁我们的加密代理安全吧!

流量特征监控

这是最难以避免的监控手段,也是 GFW 长久以来对抗机场最有力的武器。代理节点终究不是一个合法的网站,因此一旦访问人数太多,GFW 就很容易发现节点流量与实际提供服务之间的不匹配。以 Reality 为例,如果我们从某个人博客网站偷取证书,却使用代理节点看视频,GFW 很容易发现文字博客应有的文字流量和实际上的视频流量之间的不同——前者的数据包是断续少量发送,后者的数据包是大量连续发送,差距非常明显。同样,如果有很多人使用同一个代理节点,也容易触发这样的情况:个人博客的访问量理应不多,却有几十甚至数百个来自全国各地的人长时间、大流量连续访问这个服务器。

这种差别在使用人数较少(例如独自使用)时很不明显,但一旦使用人数增加,就会立即引发 GFW 的警觉,并最终在某一时刻执行封禁。对大量提供服务的机场来说,这种封禁是可预料的运营成本;但对我们决定自建加密代理的个人来说,这就是需要我们提防并且有意识避免的情况。当然,防御方法也很简单:自用的节点保持自用,不要分享给其他人。

高位端口监控

所有使用 TLS 的正常网站都监听 443 端口。然而,代理服务端却可以被配置为监听其它端口。这本来是为了给用户更多的灵活性,可是一旦 GFW 发现有 TLS 连接发往非 443 端口,就很容易引发警觉,因为这不是一个标准的 TLS 网站应有的行为。

解决方法也很简单,那就是配置代理服务器监听 443 端口。

“18 线 IDC”监控

很多人会有这样的想法:“既然 GFW 会用流量特征监控我,那我伪装成大公司主页不就好了!反正访问大公司的人很多很多,流量特征千奇百怪,而且他们在屏蔽大公司时肯定会投鼠忌器~我真是个天才!”

事实上,这种想法是完全错误且非常危险的!

具体原因是这样:大公司有自己的服务器机房,自己的网段,并且和 CDN 厂商有着密切合作。但我们个人购买租用的云服务器都是小型厂商、价格低廉的款式。在这种机房出现一家“大公司主页”,就像是大公司决定去 18 线城市开一个分部一样不合常理。因此,这种“18 线机房”中出现的大公司主页很容易遭到 GFW 怀疑,进而增大被各种监控手段“特殊照顾”的可能性。

应对方法同样简单,那就是永远不要偷窃大公司网站。GitHub 上有一个项目能自动扫描适合 Reality 偷取证书的网站,使用其中扫描到的网站作为替代。

结语

通读完本文,我们就对加密代理的基本原理有了粗略的了解。如果想要真的将其投入实践,还需要学习大量有关 POSIX 兼容系统、命令行,乃至加密代理实现程序本身的知识。实践的过程也免不了需要耗费大量的时间和精力,以及一些金钱。琳琅满目的服务提供商,还有互联网产业链的上上下下,在我们进一步深入探索加密代理时都会有所涉及。

笔者在此希望读者们不要畏难,努力向着更自由、更安全、更能保护隐私的互联网前进。最后让我们引用 XTLS 主页的铭言来结束本篇文章:

不畏浮云遮望眼 · 金睛如炬耀苍穹