为什么Gost TLS隧道的默认配置不安全
Gost隧道是一个Golang实现的方便的安全隧道工具,但是其默认配置下的TLS隧道(包括TLS、HTTP2、WSS)并不安全,这里来讨论其不安全性并加以解决。
MITM
HTTPS并不是绝对安全的,例如有时浏览网站出现
此时证书不正确,除了是网站管理员配错了证书,另一个极大的可能性就是遭到了HTTPS中间人攻击。
TLS握手流程详解
Client Hello
握手第一步是客户端向服务端发送 Client Hello 消息,这个消息里包含了一个客户端生成的随机数 Random1、客户端支持的加密套件(Support Ciphers,例如aes-128-gcm)和 SSL Version 等信息。
Server Hello
第二步是服务端向客户端发送 Server Hello 消息,这个消息会从 Client Hello 传过来的 Support Ciphers 里确定一份加密套件,这个套件决定了后续加密和生成摘要时具体使用哪些算法,另外还会生成一份随机数 Random2。注意,至此客户端和服务端都拥有了两个随机数(Random1+ Random2),这两个随机数会在后续生成对称秘钥时用到。
Certificate
这一步是服务端将自己的证书下发给客户端,让客户端验证自己的身份,客户端验证通过后取出证书中的公钥。
Server Hello Done
Server Hello Done 通知客户端 Server Hello 过程结束。
Certificate Verify
客户端收到服务端传来的证书后,先从 CA 验证该证书的合法性,验证通过后取出证书中的服务端公钥,再生成一个随机数 Random3,再用服务端公钥非对称加密 Random3 生成 PreMaster Key。
在这一步,如果客户端选择验证服务端发送的证书,且不匹配,将立即断开连接。
Change Cipher Spec(Client)
这一步是客户端通知服务端后面再发送的消息都会使用前面协商出来的秘钥加密了,是一条事件消息。
Encrypted Handshake Message(Client)
这一步对应的是 Client Finish 消息,客户端将前面的握手消息生成摘要再用协商好的秘钥加密,这是客户端发出的第一条加密消息。服务端接收后会用秘钥解密,能解出来说明前面协商出来的秘钥是一致的。
Change Cipher Spec(Server)
这一步是服务端通知客户端后面再发送的消息都会使用加密,也是一条事件消息。
Encrypted Handshake Message(Server)
这一步对应的是 Server Finish 消息,服务端也会将握手过程的消息生成摘要再用秘钥加密,这是服务端发出的第一条加密消息。客户端接收后会用秘钥解密,能解出来说明协商的秘钥是一致的。
Application Data
到这里,双方已安全地协商出了同一份秘钥,所有的应用层数据都会用这个秘钥加密后再通过 TCP 进行可靠传输。
MITM 示例
以下例子来自Wikipeda。
假设爱丽丝(Alice)希望与鲍伯(Bob)通信。同时,马洛里(Mallory)希望拦截窃会话以进行窃听并可能在某些时候传送给鲍伯一个虚假的消息。
首先,爱丽丝会向鲍勃索取他的公钥。如果Bob将他的公钥发送给Alice,并且此时马洛里能够拦截到这个公钥,就可以实施中间人攻击。马洛里发送给爱丽丝一个伪造的消息,声称自己是鲍伯,并且附上了马洛里自己的公钥(而不是鲍伯的)。
爱丽丝收到公钥后相信这个公钥是鲍伯的,于是爱丽丝将她的消息用马洛里的公钥(爱丽丝以为是鲍伯的)加密,并将加密后的消息回给鲍伯。马洛里再次截获爱丽丝回给鲍伯的消息,并使用马洛里自己的私钥对消息进行解密,如果马洛里愿意,她也可以对消息进行修改,然后马洛里使用鲍伯原先发给爱丽丝的公钥对消息再次加密。当鲍伯收到新加密后的消息时,他会相信这是从爱丽丝那里发来的消息。
爱丽丝发送给鲍伯一条消息,却被马洛里截获:
爱丽丝“嗨,鲍勃,我是爱丽丝。给我你的公钥” –> 马洛里 鲍勃马洛里将这条截获的消息转送给鲍伯;此时鲍伯并无法分辨这条消息是否从真的爱丽丝那里发来的:
爱丽丝 马洛里“嗨,鲍勃,我是爱丽丝。给我你的公钥” –> 鲍伯鲍伯回应爱丽丝的消息,并附上了他的公钥:
爱丽丝 马洛里<– [鲍伯的公钥]– 鲍伯马洛里用自己的密钥替换了消息中鲍伯的密钥,并将消息转发给爱丽丝,声称这是鲍伯的公钥:
爱丽丝<– [马洛里的公钥]– 马洛里 鲍勃爱丽丝用她以为是鲍伯的公钥加密了她的消息,以为只有鲍伯才能读到它:
爱丽丝“我们在公共汽车站见面!”–[使用马洛里的公钥加密] –> 马洛里 鲍勃然而,由于这个消息实际上是用马洛里的密钥加密的,所以马洛里可以解密它,阅读它,并在愿意的时候修改它。他使用鲍伯的密钥重新加密,并将重新加密后的消息转发给鲍伯:
爱丽丝 马洛里“在家等我!”–[使用鲍伯的公钥加密] –> 鲍伯鲍勃认为,这条消息是经由安全的传输通道从爱丽丝那里传来的。
这个例子显示了爱丽丝和鲍伯需要某种方法来确定他们是真正拿到了属于对方的公钥,而不是拿到来自攻击者的公钥。否则,这类攻击一般都是可行的,在原理上,可以针对任何使用公钥——密钥技术的通讯消息发起攻击。幸运的是,有各种不同的技术可以帮助抵御MITM攻击。
简要总结
- 证书由服务器向发起请求的客户端传送,客户端选择验证与否。
- MITM 欺骗通信的两端,让两端都以为在和对方加密通信,实则是和中间人通信。
- 若客户端验证证书,MITM失效。
Replay Attack
Replay Attack是利用重新向服务端发送拦截到的数据包(修改某系字节),获得服务端的回包(通常是“密码错误”之类),从而通过回包特征知晓服务器的该端口部署了什么服务。
值得庆幸的是,TLS天然防范Replay Attack。
但是若不是TLS加密的服务,防范Replay Attack也很简单,只需在iptables设置只允许客户端IP访问即可
假设客户端IP为x.x.x.x,首先允许他的访问(对于指定端口)
1 | iptables -A INPUT -p tcp -s x.x.x.x --dport port -j ACCEPT |
然后拒绝任何IP访问(直接丢包,不给予任何回包)
1 | iptables -A INPUT -p tcp --dport port -j DROP |
不要忘记iptables是顺序查询规则的。
默认证书特征明显
如图,默认证书不是由受信CA颁发,且颁发者/目标均显式地显示为gost,特征明显。
解决
申请自己的证书
这要求你有一个自己的域名,然后去受信任CA处申请证书。通常你可以申请到免费的DV证书,例如前往Trust Asia网站可以获得免费一年期的亚洲诚信DV证书。
部署证书在服务端
选一个文件夹存放证书,例如/home/crt
。
将证书传入该文件夹。
如果你的证书只有.crt
和.key
格式的,使用openssl工具转换:
1 | openssl x509 -in domain.crt -out cert.pem |
1 | openssl rsa -in domain.key -out key.pem |
这里命名为cert.pem
和key.pem
是因为Gost会默认读取执行目录下的这两个文件作为证书。
然后运行:
1 | gost -L relay+tls://:listenPort/:servicePort |
部署证书在客户端
我们同样将证书传入/home/crt
,但客户端只需要.crt
不需要私钥。
同样的,不是.pem
格式的话请先行转换。
运行:
1 | gost -L tcp://:listenPort -F="relay+tls://serverIP:serverPort?secure=true&ca=/home/crt/cert.pem" |
secure=true
指定了必须验证证书。
ca=$PATH
锁定了证书,服务端传回的证书必须是它,否则断开连接。
完成
Enjoy your work.