SOCKS代理协议漫谈

SOCKS 代理协议是网络上使用非常普遍的一种协议,最近因为想要自己搭建一个穿透局域网安全网关的代理,所以,顺便仔细学习了一下 SOCKS 的相关资料,还算有点意思,特此记录一下。

SOCKS 协议的故事

话说,一个叫做 David Koblas 的系统管理员,他在 MIPS Computer Systems 公司工作。就是这个人发明了 SOCKS 代理协议。MIPS 公司在 1992 年,被一家叫做 Silicon Graphics 的公司给控制了(应该是收购了吧),这家公司后来把自己的品牌名改为了 SGI(一家很高端的软硬件制造商,2009年4月破产了)。也就是在这一年 1992 年,David 在 Usenix Security Symposium 安全研讨会上,发布了一篇论文,名字就叫 SOCKS,将此协议公之于众。

最早,整个互联网采用的是盲目信任的方式进行连接和组织的。当时,专家们在聚会的时候,讨论的都是怎么让网络更加简单和高效,一台主机在网络上到底怎么才能更可靠的被别的主机所连接。一直到 1988 年,一个叫做 Morris 的蠕虫,给整个互联网一记重拳,后来大家讨论将一个子网络接入到互联网的时候,安全也成了一个必须要讨论的重要话题。

不过,提升网络的安全性可没有什么容易的办法,想出来的大部分办法都是通过减少内网服务器向公网暴露的机会,来最小化攻击的概率,也就是我们常喜欢用的“最小原则”,如果没有必要,就不进行授权。很多网络在接入互联网的时候,都选择了单一出入口的方式。将本地的子网络,置于防火墙的保护之后,再接入到互联网,如此一来就极大减少了被攻击的机会,网络的安全性自然就提高了。

但是本地网络置于防火墙之后,再访问外部网络的资源就会非常不便。为了解决这个问题,人们想出了各种方案。内外网隔离(笨拙,使用极其不便,但是维护成本很低),单一机器授权(只有一台机器可以和外网进行双向访问,使用仍然非常不便,而且维护成本高昂,因为有很多用户的访问权限要分配和收回),还有安全路由器(常见的一种策略是允许所有的出口流量,但是对进口流量禁止所有 1024 以下端口的访问,这种策略的问题是,一旦路由被攻破,整个网络就置于威胁之下)。

OSI 7层网络模型

在这些方案之外,代理防火墙(Proxy Firewall)解决方案就被提出来了。路由器是在 OSI 模型的“网络层”进行安全过滤,可以降低客户端的成本,但是不够完善,维护起来也比较困难,估计当年的硬件不像现在可以随意修改配置吧。代理防火墙的方案,平衡了使用的便捷和维护的复杂度,是一种折衷。

代理防火墙工作在 OSI 模型的“会话层”,也就是“传输层”和“应用层”中间的地方。著名的 SOCKS 就是这样一种“解决方案”。它是一种非常轻薄的解决方案,在客户端,提供了一套开发类库,叫 SOCKS 库,对照着标准 socket 的 API,提供了五个 API 函数,名字跟 socket 的一样,只是用 R 作为前缀。在服务器端,提供了一个叫做 sockd 的伺服软件,这个软件部署在防火墙系统所在的一台主机上,通过简单的配置文件就可以完成应用层的过滤,允许或拒绝哪些目的地址和端口被接入,是非常容易维护的,给网络管理员带来了极大的方便。

在 1992 年公布之前,SOCKS 解决方案已经在 MIPS 内部使用了长达三年之久,没有遇到明显的问题和瓶颈,所以 David 认为这是一个久经烤验的成熟方案。

SOCKS 的版本发展

上面的故事里,我们可以看到 SOCKS 最初提出的时候,其性质是一个解决方案,包含一个类库和一个服务端后台伺服程序。但是,NEC 公司的李英达?(Ying-da Lee),看到了它的优美之处,把它提炼出来,发展成了一种协议,变得更加通用,而且,大家都可以根据协议提出自己的实现。这位李同学,提出了 SOCKS 协议的第四个版本。也是流传非常广泛的版本。

从李同学开始,SOCKS 的定位也变得非常明确,就是在防火墙服务器上,提供一种 TCP 会话的转发,允许用户可以透明的穿透防火墙的阻拦。这种协议的优势在于,它完全独立于应用层的协议,可以用在很多的场景,telnet,http,ftp 都不在话下,并且可以在 TCP 会话开始之前,完成访问权限的检查,之后只要做来回往复的转发即可。而且由于此协议完全不关心应用层的协议,所以应用层通信可以加密,保护自己通信的内容不被代理所看到。

SOCKSv4 开始,这个协议得到了非常广泛的应用,所以,广大群众对这个协议进行了扩展,就有了 SOCKSv4a 版本,这个版本只对 v4 进行了比较小的改动,主要是允许在协议头里填充域名,代替IP地址,用以防止客户端不能正确解析出目标IP的场景。(举个例子,某个局域网有一台服务器,域名是 test.oa.com,与之对应的 IP 地址也是内网的,这种情况下,外部用户是很难知道到底 IP 地址是什么的,因为没有登记在公网的 DNS 服务里面)到这里后,SOCKS 就成了一种非常通用的双向通信的轻量级代理协议了。也有了大量的标准实现。得到了海量的需要网络连接的应用程序的支持,成为了“电路级网关”(circuit-level gateway)的事实标准。

现在更加流行的 SOCKSv5 版本就是一个更加完善的版本了,主要增加了:

强力的身份验证方案

验证方法的协商机制

地址解析的代理

UDP协议应用的代理支持

SOCKS 协议的内容

SOCKSv4的内容

上面的插图展示了SOCKS协议第四版的内容,可以看见,这里展示的是前两个包体,客户端发起,服务器回复,连接成功后,后续的操作就透明了。

SOCKSv4a 对第 4 版的扩展,非常小,就是增加了一个域名,只要在 IP 那个字段,前三个字节都填 0x00,就会由服务器来负责解析正确的地址,并利用回包空闲的两个字段来返回正确的 IP 地址和端口号码。

到了现在普遍使用的 SOCKSv5,协议变复杂了很多。首先,因为有强力的验证,而且支持多种验证方法,就有了一个协商验证方法的过程,然后,进行身份验证,最后再进行通信指令。

SOCKS5 的协商过程

上面的图,展示了 SOCKS5 连接的协商过程。协议的具体内容,对应着上面的握手环节,增加了几种不同的包体:

SOCKS5 的协议包体结构

SOCKS 协议的不足

从上面的 SOCKS5 的时序图上,我们可以看到,由于承担了额外的验证协商的功能,导致 SOCKS5 在建立的时候,需要额外消耗多达三次握手,如果不需要验证身份,也需要两次握手,这就增加连接时候的延迟。

另外,因为 SOCKS5 协议本身完全不关注应用层的内容,所以,客户端和目标服务器的通信,加密完全依赖客户端和服务器的通信协议,如果服务器使用的是 HTTP 协议,那么通过代理走的流量,就相当于是明文在内网传输。安全性上不是很高。

不过,我们可以通过在外面包裹一层 TLS 来解决这个问题,就形成了 SOCKS5 over TLS 的解决方案,有效加密了通信的内容。TLS 负责加密连接,SOCKS5 负责代理协议控制。

互联网上已经公布了 SOCKSv6 的草案,主要内容就是SOCKS5的问题修复,第一个就是多次握手已经不太适用于移动互联网和卫星通信等网络环境,需要被优化。客户端会尽可能多的发送信息给服务器,并且要求创建 socket 之前,不等待验证的结论;连接请求模仿 TCP Fast Open 的语义,具体就是在连接请求的包体里,带上一部分载荷,连同第一个SYN包一同发给服务器;根据选项可以不向后兼容;可选支持 0-RTT 的验证方法(一个IP包体到达服务器再返回客户端的过程消耗,叫 1-RTT)。

不过,目前来说,还没有得到广大软件厂商的支持。而且据说 TLS 1.3 也在积极探索 0-RTT 的解决方案,不过也还在协议草案的早期。

参考文献:

SOCKS 维基百科

曾经提供 SOCKS 服务的官网(已经关闭了只有 archive) https://web.archive.org/web/20050204010949/http://www.socks.permeo.com/AboutSOCKS/SOCKSOverview.asp

firewalls and fairy tales 评论文章 https://www.usenix.org/system/files/login/articles/1045-firewalls.pdf

SOCKS 1992年原始论文

各种加密协议的简单对比 https://medium.com/@Blankwonder/

SOCKS Protocol Version 6

↑↑↑长按图片识别二维码关註↑↑↑