一口气搞明白有点奇怪的 Socks 5 协议以及 HTTP 代理

2022-10-10

前言

其实一直想一口气将某个东西上下左右全部解释清楚,就像之前分享命令行的相关的东西,因为善用命令行可以极大提高工作生活效率,然而现实是混沌的,其中会有很多点,比如内心的舒适区,比如人们潜意识中会认为系统化的长内容或者稍微复杂点的内容更有压力,所以如今互联网上短轻内容大行其道,几乎全是部分化的内容和碎片化的内容。这点很有意思,以后单独做视频分析。

回到正题,让我们一起搞明白这个 Socks 5 代理协议,为了方便理解咱们会同时解释下 HTTP 代理协议,以做对比。先解释协议,以及针对常见的一些问题做些说明,然后说下这两个协议以前的应用场景,以及现在的应用场景。

要代理的内容一般分为基于 TCP 的内容和基于 UDP 的内容。

HTTP 代理

咱们先说一下 HTTP 代理。HTTP 代理其实分两种,一种是反向代理,比如 nico 和 nginx 就是这种,这里不展这种代理。另一种是隧道代理,咱们主要展开下这种代理,它能代理任何基于 TCP 的内容,这里注意有个误区,很多人以为 HTTP 代理只能代理 http:// 内容,其实也可以代理 https:// 以及任何基于 TCP 的内容,但是不能代理 UDP 内容,等会展开下就知道为什么不能代理 UDP 内容了。

假设有一个 HTTP Proxy Server

1.2.3.4:8010

某客户端想代理的地址

google.com:443

咱们分配一下角色,这里某客户端可能是 Chrome 浏览器,也可能是 curl 命令。而 HTTP Proxy Server 就是服务端。

客户端和服务端的交互如下

第一步:客户端 -> 服务端,发送要代理的地址

CONNECT google.com:443 HTTP/1.1

第二步:服务端 -> 客户端,响应结果

HTTP/1.1 200 Connection established

然后隧道就建立了,客户端和服务端就可以通过这个隧道互相传输内容了。

问题一:域名在哪儿里解析

被代理的地址是由客户端传递给服务端的,客户端如果传递的是域名地址,那么域名将在服务端解析,当然也有可能客户端在本地预先解析出来域名的 IP,然后传递给服务端 IP 地址。所以域名在哪里解析,完全取决于最起初发起请求的客户端这个角色。

问题二:不能代理 UDP 内容

如前面看到的交互步骤,整个交互内容并没有区分 TCP 或 UDP 的字段,对吧?所以其只能代理 TCP 内容。当然有同学会说,在服务端强制当做 UDP 内容来处理不就行了,那其实就变成另外一种代理协议了。

问题三:传输内容没有加密

这个其实是相对于 HTTP Proxy Server 来说的,它收到内容后并没有二次加工。当然客户端如果要求的代理的内容是 HTTPS 内容,内容已经在客户端进行 TLS 加密了,所以再次强调,这个其实是相对于 HTTP Proxy Server 来讲,即,HTTP 代理是一个非加密代理协议。

Socks 5 代理

咱们再说一下 Socks 5 代理。它能代理任何基于 TCP 的内容,也能代理 UDP 内容。但其实很多 Socks 5 代理服务端并没有实现 UDP 的支持。你可以使用 brook testsocks5 命令测试某个 Socks 5 Server 是否支持 UDP。

假设有一个 Socks 5 Server

1.2.3.4:1080

某客户端想代理的 TCP 地址

google.com:443

咱们分配一下角色,这里某客户端可能是 Chrome 浏览器,也可能是 curl 命令,也可能是某游戏客户端。而 Socks 5 Server 就是服务端。

客户端和服务端的交互如下,为了方便理解这里将原始协议抽象为人类易读的内容。

第一步:客户端 -> 服务端(TCP),询问协议版本和认证方式

Ask for Version and Auth method

第二步:服务端 -> 客户端(TCP),响应协议版本和认证方式

Response Version  and Auth method

第三步:客户端 -> 服务端(TCP),如果服务端需要认证,发送认证信息

Auth Info

第四步:服务端 -> 客户端(TCP),如果服务端需要认证,响应认证结果

Auth Success

第五步:客户端 -> 服务端(TCP),发送要代理的地址

TCP google.com:443

第六步:服务端 -> 客户端(TCP),响应结果

Response OK

然后隧道就建立了,客户端和服务端就可以通过这个隧道互相传输内容了。我们注意到,客户端和服务端之间全程走的都是 TCP 协议。

某客户端想代理的 UDP 地址

google.com:443

客户端和服务端的交互如下,同样为了方便理解这里将原始协议抽象为人类易读的内容。

第一步:客户端 -> 服务端(TCP),询问协议版本和认证方式

Ask for Version and Auth method

第二步:服务端 -> 客户端(TCP),响应协议版本和认证方式

Response Version  and Auth method

第三步:客户端 -> 服务端(TCP),如果服务端需要认证,发送认证信息

Auth Info

第四步:服务端 -> 客户端(TCP),如果服务端需要认证,响应认证结果

Auth Success

第五步:客户端,自己准备好一个 src 地址,等会用来通过 UDP 和服务端交互

Prepare a src UDP address

第六步:客户端 -> 服务端(TCP),告诉服务端自己想要使用 src 来进行 UDP 通讯

I want send UDP data from src

第七步:服务端,准备一个 dst 地址,等会用来接收客户端发送的 UDP data

Prepare a UDP dst server address

注意:我们的 Socks 5 Server 地址是 1.2.3.4:1080,但是服务端准备的这个 UDP Server 可以不是 1.2.3.4:1080,可以是任何地址,比如 1.2.3.4:1081,甚至可以是别的机器上的一个 Server,比如 5.6.7.8:6789。

第八步:服务端 -> 客户端(TCP),告诉客户端可以往 dst 地址来发送 UDP data

Please send UDP data to dst

第九步:客户端 src -> 服务端 dst(UDP),开始代理 UDP data

Please send this UDP data to google.com:443

然后 UDP 隧道就建立了,客户端和服务端就可以通过这个隧道互相传输 UDP 内容了。

问题一:域名在哪儿里解析

被代理的地址是由客户端传递给服务端的,客户端如果传递的是域名地址,那么域名将在服务端解析,当然也有可能客户端在本地预先解析出来域名的 IP,然后传递给服务端 IP 地址。所以域名在哪里解析,完全取决于最起初发起请求的客户端这个角色。

问题二:可以代理 UDP,但是前面沟通仍然用的还是 TCP

也就说 Socks 5 可以代理 UDP,但是开始代理 UDP 之前,仍然需要先通过 TCP 进行一系列的沟通。

问题三:代理 UDP 时,可能需要保持 TCP 连接不断开

前面我们知道,要想代理 UDP,必须先建立一个 TCP 连接来进行一系列沟通。根据标准协议说明,代理 UDP 期间,这个预先沟通的 TCP 连接,可以分为两种情况。一种是沟通完断开即可。一种是沟通完保持连接。服务端可以自行决定根据 TCP 连接是否断开这个状态,来允许或拒绝代理 UDP。所以如果客户端和服务端没有预先沟通的情况下,客户端应该维持这个 TCP 连接不断开。

问题四:我可以使用 brook relay 来中继 Socks 5 Server 吗

我们知道 brook relay 可以中继任何基于 TCP 和 UDP 的服务,只需要指定一个 from 地址和一个 to 地址即可。但是根据前面 Socks 5 代理 UDP 协议的第七步,只需要 Socks 5 Server 将告知客户端的 UDP Server 指定为 relay server 的地址即可。如果你使用的是 brook socks5,那么可以用 --socks5ServerIP 参数来指定 UDP Server 的 IP。

问题五:传输内容没有加密

这个其实是相对于 Socks 5 Server 来说的,它收到内容后并没有二次加工。当然客户端如果要求的代理的内容是 HTTPS 内容,内容已经在客户端进行 TLS 加密了,所以再次强调,这个其实是相对于 Socks 5 Server 来讲,即,Socks 5 代理是一个非加密代理协议。

问题六:我们应该以什么态度看待协议的标准

一个协议从草案到正式会经过很长时间,而对标准的遵守也会对减少差异和碎片化有好处。

问题七:NAT 类型

涉及到 UDP 代理,一般就会考虑 NAT 类型,我们知道有四种 NAT 类型,Full cone NAT,Address-Restricted cone NAT,Port-Restricted cone NAT,Symmetric NAT。Symmetric 是安全性最高的 NAT 类型,所以目前 brook 采用的是此 NAT 类型。关于更多关于 NAT 的知识可以自行搜索

HTTP 代理和 Socks 5 代理的应用场景

在很久以前,互联网对加密还不太重视,那个时候,大家在远端搭建一个 HTTP 代理或 Socks 5 代理,然后在本地应用上配置上远端的代理地址,就这么很快乐的用着。

然而某天,人们觉醒了,我本地通过这两个代理协议向远端发送的数据没有加密,这样岂不是路上的人们都可以看到我传输的数据。这有点不快乐。所以比如强加密无特征协议的 brook 协议诞生了。

因为经过远古时代的发展,很多应用比如 Chrome 浏览器,都支持配置 Socks 5 Server,所以像 Brook 客户端的 Proxy 模式,会在本地建立一个 Socks 5 Server,这也意味着 HTTP 代理和 Socks 5 代理的应用场景基本上从以前的远端运行模式过渡到本地运行模式。

HTTP 代理和 Socks 5 代理的作用域

前面我们讲到这两种协议从远端运行模式过渡到了本地运行模式,无论是运行在哪儿,它们的角色都是服务端,而作为客户端角色是否走这个代理,完全取决于客户端角色。比如配置到系统代理的话,Chrome 会走代理,而终端不会走代理。

这里鼓励大家换 M1 版 Mac,也算是趋势,可以使用 iOS 版的 Brook 客户端Brook 手机客户端默认均为虚拟网卡模式,而非 Proxy 模式。

同样,手机端也是一样,你在手机端配置了系统代理,但是 app 作为客户端角色可以自行选择是否走代理。很多抓包软件采用配置系统代理模式,app 可以很容易就绕过去了。

如果你有抓包需求,可以尝试下我的 mitmproxy helper 以及 Wireshark Helper 抓包工具,均是从虚拟网卡从 IP 包一层层组装协议,而非系统代理模式。以支持我的工作。

讨论