我司买的是有带宽保证的专线,然而还是怕网络流量分析(不是怕被墙,是怕被监听)。据说应对方法很简单:一个全零的源源不断的数据流作为载波,把欲传输的信息调制在上面,再加密传输出去。这样在网络上看到的就是以专线带宽不停发送的大 UDP 包,欲传输信息的流量特征不会在网络上有任何的体现。调制就是个异或操作,不会造成带宽损失。顺便还能监控专线质量,如果带宽缺斤短两了马上就能发现。
理论上,只要把欲传输的信息异或调制在一个 stream cipher 上面,直接封包发送出去就行了。然而实际网络中是有丢包和错包的,发生丢包和错包的时候对面就解不出来了,所以要每个包独立加密,并且有 HMAC 验证的机制,就像 shadowsocks 的做法。
当然一般人没有买专线这么土豪,而且长时间全速发送的 UDP 流还是容易被认为不正常,因此可以封装到 HTTP 请求里面。在网络监控者看来,就是在上传一个大文件。请求的内容部分作为载波,也就是没有数据要传的时候是全 0,有数据要传的时候就是数据本身,再加密一下发出去。加密算法保证了发出去的数据在统计上没有信息量,而载波保证了数据包的大小和间隔与要传输的信息无关。如果觉得一个 HTTP 请求不够快,还可以多搞几个并发连接。我不知道现在有没有开源工具能做到这件事。
怕没解释清楚,写一点伪代码,这里 plainStream 可以来自一个虚拟的网络设备(例如 OpenVPN 的 tun0),也就是把该虚拟网卡收到的 IP 包想成一个数据流。这个假定了 HTTP 连接是可靠的。由于明文流量可能超过载波带宽,plainStream 里要有一个 buffer,buffer 满的时候,虚拟网卡就要丢包了。
def SendConfidentialStream(plainStream, server, preSharedSecret):
req = CreateHttpRequest(server)
req.SendHttpHeader(CreateFakeHTTPHeader())
enc = CreateEncryptStream(preSharedSecret)
while plainStream.NotEof():
carrier = GenZeroBlock()
if plainStream.HasPendingData():
plain = plainStream.ReadPendingData(len(carrier))
carrier[0:len(plain)] = [chr(ord(p) ^ ord(c)) for p,c in zip(plain,carrier)]
encrypted = enc.EncryptBlock(carrier)
req.SendBody(encrypted)
req.CloseConnection()