不一

Shadowsocks协议解析

字数统计: 2.3k阅读时长: 8 min
2020/06/20 Share

Shadowsocks过程

Shadowsocks是一个代理协议,以下简称SS。当然基于SS协议催生出了许多的代理软件,例如Shadowsocks-NG(MacOS平台)、Shadowrocket(iOS平台)等。不过总归来说SS的流程是一致的:

上图简述了SS在整个数据传输中的位置,包含了SS客户端和SS服务端两个部分,一般来说SS客户端是安装在本地的软件,而SS服务端则是运行在VPS上的软件。如果没有SS的话,上图就成了没有代理的用户与目标服务器(例如Google.com)的直接通信。下面一SS代理下的HTTP请求介绍上述过程:

  1. 浏览器要访问Google.com,便生成HTTP请求。由于设置了代理,所以不会直接发送到Google服务器,而是发送到SS客户端(例如127.0.0.1:1086)。
  2. SS客户端把HTTP请求封装成SS请求(后面详细介绍),包含协议封装和加密过程,发送给SS服务器。
  3. SS服务器收到SS请求并解析,包含解密和协议解析过程,获得原始HTTP请求。
  4. SS服务器连接Google服务器,发送用户的原始HTTP请求,并获取Google返回的HTTP响应。
  5. SS服务器对HTTP请求进行加密,并发送到SS客户端。此处没有协议封装。
  6. SS客户端收到SS服务器的响应,进行数据解密得到HTTP响应,发送到浏览器以完成HTTP请求。

Shadowsocks协议

接下来会在字节层面对上述过程进行详细介绍。由于SS在处理TCP连接和UDP报文所使用的协议有所差异,因此也会分开介绍。

TCP连接

TCP连接模式主要包括HTTP、HTTPS和Socket等应用层协议,这些协议有所差异比如说有的协议有握手,有的协议却只有一来一回的数据交互,有的协议可能会保持长时间的连接和数据传输。。但这些对于SS来说都是一样的,因为它们都不过是数据(payload),而SS要做的无非是创建两个通道,一个用于把客户端数据传输到服务端,一个用于把服务端的数据传输到客户端

SS请求

TCP握手很熟悉了,握手的目的是建立数据链路。SS也有握手过程,目的就是建立上述的数据链路,不过这个过程只有半次握手,即SS请求。SS请求的主要功能是建立代理链路,因此需要告诉SS服务器要代理访问的远程服务器地址(如果不知道IP地址的话,给域名也可以),然后可以顺便携带一部分传输数据。根据地址类型的不同,SS请求会有3种形式:

  1. 通过SS请求传输目标服务器的IPv4地址:
  2. 通过SS请求传输目标服务器的IPv6地址:
  3. 在不知道目标服务器IP地址的时候(比如DNS被劫持),直接传输目标服务器的域名:

客户端加密

SS客户端把SS请求组装好了,在向SS服务器传输之前还需要进行加密,否则用户的数据就在网络上明文传输了,这个是很不安全的。下面举例使用SS中最常用的加密方案AES-256-cfb方式进行数据加密,大致步骤如下:

  1. 密钥生成:AES-256意味着其密钥长度是256bit,但用户密码有长有短,而且都是可见的ASCII,把密码作为密钥来用显然安全性是不够的,所以需要根据密码生成安全性达标的256bit长度密钥。SS中使用MD5对密码生成16byte长度的消息摘要,即可作为密钥使用。
  2. IV生成:原始AES加密下,对于相同的明文会生成相同的密文,这也是安全性的一种缺陷。AES-cfb通过引入随机数IV的方式解决该问题,即在随机数IV的加持之下可以实现相同的明文生成不同的密文。在AES-256-cfb中,IV是一个16bit的随机数。
  3. 加密:有了密钥、加密算法、IV,就可以生成加密器,并对数据进行加密了。在此数据就是上述所组装的SS请求,得到密文cypher。为了让SS服务端可以解密,还需要把IV也一同发送,所以最终生成的数据如下:

服务端解析

SS服务器和SS客户端所共享的信息有密码和加密方式,这是配置过SS服务器的人都知道的,在收到SS客户端发送的SS请求后,SS客户端进行解析的过程如下:

  1. 密钥生成:根据密码生成密钥,由于和客户端拥有相同的密码和密钥生成算法,所以生成的AES-256-cfb密钥也是一样的。
  2. 获取IV:从SS客户端接收到的数据前16bit提取出来就得到了AES-256-cfb的IV。
  3. 解密:根据密钥、加密算法和IV生成解密器,对收到的数据进行解密,就得到了SS请求明文。
  4. 地址解析:根据SS请求的第1个Byte得知目标服务器地址的类型(IPv4,IPv6或域名),并根据相应的规则进行解析得到其地址与端口。
  5. 代理请求:说到底SS就是一个代理协议,代理的意思就是代替客户端对目标服务器发起请求。通过地址与端口连接目标服务器,并发送解密后的数据,完成请求。

服务端响应

SS服务器收到目标服务器的数据,把数据传输给SS客户端,也需要进行加密,不同的是不需要组装协议了。所以该过程就比较简单:

  1. 生成IV:虽然也是随机数,但该IV与SS客户端发送请求时生成的IV不相等,需要再生成一个。
  2. 加密:根据新生成的IV、密钥和加密算法生成加密器,对目标服务器响应的数据进行加密。同样为了SS客户端可以解密,也需要把IV连同密文一起发送给客户端,最终发送的数据如下:

客户端解析

SS客户端收到服务端的数据,通过提取IV就可以生成响应的解密器,对数据进行解密,从而完成用户与目标服务器的通信。
上述的加密器和解密器都是和连接绑定的,即一次TCP连接代理通信只需要互相发送一次IV用于双方创建解密器,而后续的通信内容都只有包括密文部分,使用解密器进行解密即可。
所以其实SS协议的过程是非常简单的,以至于在画下图的时候都不知道有什么可以加上的东西。

UDP连接

UDP模式在SS中通过UDP转发选项开启,用于代理UDP流量如DNS查询、即时语音通信等。

UDP模式和TCP模式差异

SS中TCP和UDP大同小异,但由于UDP之于TCP是无连接协议,因此简单探讨一下UDP模式无连接下的一些区别:

  1. TCP连接作为基本单位:在TCP模式中,从握手到挥手的过程就是一次连接,因此一次连接是SS中的基本单位,所以一次连接对应上下行的2个加密通道、2个IV。所以一次TCP连接中无论有多少次数据交互,只需要一次握手
  2. UDP数据分组作为基本单位:在UDP模式中没有连接的概念,其基本单位是每个数据包,所以每个数据包对应1个IV和加密解密器。可以说在UDP模式下有多少个数据包,就会发生多少次SS握手。
  3. 连接方式差异:因为UDP是无连接,所以在socket编程时通常不会像UDP连接一样,先connect然后才数据读写,而是直接进行数据包的收发。
  4. 方向性:TCP模式种中总是SS客户端向SS服务器发起握手,但在UDP模式中由于没有连接的概念,所以每个数据包都是平等的自带握手信息,即使SS服务器向SS客户端发送的数据也是。

UDP下的握手协议

UDP模式中每一个数据包都具有相同格式,无论是从SS客户端发往SS服务器,还是从SS服务器发往SS客户端,其格式与上述SS请求相同:

所以不仅SS客户端会把需要代理访问的目标服务器的地址信息告诉SS服务器,SS服务器也会把目标服务器返回的数据连同其地址信息一同返回SS客户端。这主要是因为UDP模式下是没有连接的概念,所以每条数据需要自带一些额外信息,就像每次进商场都要戴口罩测体温一样。

后记

学术交流而已。

CATALOG
  1. 1. Shadowsocks过程
  2. 2. Shadowsocks协议
    1. 2.1. TCP连接
      1. 2.1.1. SS请求
      2. 2.1.2. 客户端加密
      3. 2.1.3. 服务端解析
      4. 2.1.4. 服务端响应
      5. 2.1.5. 客户端解析
    2. 2.2. UDP连接
      1. 2.2.1. UDP模式和TCP模式差异
      2. 2.2.2. UDP下的握手协议
  3. 3. 后记