目录
最近在研究tcp 选择timestamp中,发现了其中功能之一是用来计算往返时间(rtt)。经过http隧道代理的学习后,我发现这个rtt在特定情况下可以用来识别用户是否使用了隧道代理。
原理
tcp隧道在创建时和建立后这2个状态中,会出现2种数值的rtt,如果可以准确的捕获,以此为依据则可以断定该访问使用了隧道代理。
-
proxy 和 server 的rtt
proxy 和 server 进行tcp握手时,可以取得对应rtt
-
clinet 和 server 的rtt
隧道建立后,数据由clinet经过proxy转发至server,此时可以获得对应rtt
-
https服务端的区分方法
访问https服务会出现2种握手,TCP握手和TLS握手。以此为标志可以取得2种rtt。
我的测试环境:
- 服务端是https
- 客户端与代理有100ms延迟
未经隧道的正常的https请求
简化示意图
clinet server
1 ===== SYN ======>
2 <==== SYN,ACK =======
3 ===== ACK ======>
4 ===== Clinet hello ======>
5 <==== ACK =======
6 <==== Server hello =======
7 ===== ACK ======>
8 ===== TLS handshake ======>
……
……
数据和分析
通过客户端抓ACK包,可以得到以下数据:
其中 rtt 由tcp的timestamp计算,clock由系统时间戳计算得到。
"deltas": [
"$rtt=178, clock=0.17799997329712", //3-1
"$rtt=0, clock=0.002000093460083", //4-3
"$rtt=171, clock=0.16799998283386", //7-4
"$rtt=10, clock=0.0099999904632568" //8-7
],
- 其中rtt=178和rtt=171,可以约为client到server的往返时间。
- 其中rtt=10,为第二个clinet TLS握手包距离上个ACK包的差值,
在正常连接中可以视为握手的验证和计算时间(猜的)。
这个时间在手机中会变大,估计是手机性能影响。
经隧道的https 请求
示意图
(简化并忽略隧道握手)
clinet proxy server
1 ===== SYN ======>
2 <==== SYN,ACK =======
3 ===== ACK ======>
4 ===== Clinet hello ======>
5 ===== SYN ======>
6 <==== SYN,ACK =======
7 ===== ACK ======>
8 ===== Clinet hello ======>
9 <==== ACK =======
10 <==== Server hello =======
11 <==== Server hello ======= ===== ACK ======>
12 ===== ACK ======>
13 ===== TLS handshake ======> ===== TLS handshake ======>
……
……
过程简述
- 1-3
为 client 和 proxy 建立tcp链接
- 4
隧道建立后,发送TLS Hello 握手包到proxy
- 5-7
为 proxy 和 server 建立tcp链接
- 8
与server建立tcp链接完成后,转发TLS Hello 握手包
- 9-13
隧道转发数据,其中TCP的ACK确认包不会被转发。
特别部分
- 11-12:
proxy会在收到 Server hello 的tcp包后,会直接给server发送ACK确认包。
同时,把数据转发给client。
而client收到Server hello 的tcp包后,ACK确认包发给的是proxy。
- 13
client收到Server hello 的tcp包经过验证和计算后,往proxy发送后续的握手包。
数据和分析
通过在服务端客户端抓ACK包(实际就是 proxy 到 server ),可以得到以下数据:
其中 rtt 由tcp的timestamp计算,clock由系统时间戳计算得到。
"deltas": [
"$rtt=159, clock=0.15899991989136", //7-5
"$rtt=0, clock=0.00099992752075195", //8-7
"$rtt=163, clock=0.16199994087219", //11-8
"$rtt=109, clock=0.10900020599365" //13-11
],
-
其中rtt=159和rtt=163,可以约为proxy到server的往返时间。
-
其中rtt=109,为 client 到 proxy 的往返时间
对比正常的请求,这个【13】的TLS握手包,proxy需要等clinet返回才可以发送。因而多了clinet到proxy的时间,忽略严重和计算部分,可以约为client 到 proxy 的往返时间。
总结
通过以上思路,https服务端通过捕获到客户端TLS握手包往返时间去判断隧道代理是可以实现的。
当然部分情况是会尝试误判:
- 客户端是移动设备,TLS握手包的验证+计算时间会太大以至于干扰往返时间去判断
例如手里的苹果xr,该时间约为20ms。远古时代里的mi5,则可以到32ms。
如果再配合浏览器验证一次算力,也是有可能减少该值的干扰。
- 如果proxy到clinet直接的延迟足够低,那么也无法识别
例如我用PC做SOCKS5转发,在同个wifi下,用手机进行隧道连接,那么得到的数据与在PC中的几乎一致。
不过实际使用隧道转发还能可如此低延迟链接,该场景我想不太出来。难道是专门在代理ip池对应地区建个机子?
相反,对于单机内使用ip代理做爬虫的类似的操作,都应该能准确的识别。
其他思考
关于服务端是不是一定要https才行这个问题。其他类型的协议是不是也可以比如http,ws,未作验证,纯脑洞。
http + 302 跳转测试
本地大概测试一下看数据应该可行。302 跳转是为了让上个连接走一个完整的TCP。
其中四次挥手中的2个确认包会被隧道直接返回。
测试环境为本地,TCP隧道延时是自己强加的,为300ms,不确定实际运行是否也是如此数据,有空再试。
直接连接
"deltas": [
"$rtt=9, clock=0.002000093460083",
"$rtt=10, clock=0.0099999904632568",
"$rtt=4, clock=0.0039999485015869",
"$rtt=0, clock=0",
"$rtt=0, clock=0"
],
"info": [
"50775 \u2192 80 [SYN] Seq=0 Win=65535 Len=0 MSS=1460 WS=32 TSval=1401643228 TSecr=0 SACK_PERM=1",
"50775 \u2192 80 [ACK] Seq=1 Ack=1 Win=131744 Len=0 TSval=1401643237 TSecr=124477866",
"GET \/test.php HTTP\/1.1 ",
"50775 \u2192 80 [ACK] Seq=408 Ack=218 Win=131520 Len=0 TSval=1401643251 TSecr=124477880",
"50775 \u2192 80 [ACK] Seq=408 Ack=219 Win=131520 Len=0 TSval=1401643251 TSecr=124477880",
"50775 \u2192 80 [FIN, ACK] Seq=408 Ack=219 Win=131520 Len=0 TSval=1401643251 TSecr=124477880"
]
300ms的TCP隧道
"deltas": [
"$rtt=0, clock=0",
"$rtt=36, clock=0.03600001335144",// 接近设置的300ms
"$rtt=3, clock=0.0019998550415039",
"$rtt=0, clock=0.0010001659393311",
"$rtt=27, clock=0.026999950408936"//接近设置的300ms
],
"info": [
"65159 \u2192 80 [SYN] Seq=0 Win=65535 Len=0 MSS=65495 WS=256 SACK_PERM=1 TSval=123950403 TSecr=0",
"65159 \u2192 80 [ACK] Seq=1 Ack=1 Win=2619136 Len=0 TSval=123950403 TSecr=123950403",
"GET http:\/\/192.168.2.121\/test.php HTTP\/1.1 ",
"65159 \u2192 80 [ACK] Seq=458 Ack=218 Win=2618880 Len=0 TSval=123950442 TSecr=123950441",
"65159 \u2192 80 [ACK] Seq=458 Ack=219 Win=2618880 Len=0 TSval=123950442 TSecr=123950442",
"65159 \u2192 80 [FIN, ACK] Seq=458 Ack=219 Win=2618880 Len=0 TSval=123950469 TSecr=123950442"
]
关于代码或demo
proxy guess demo
最后个人水平有限,有理解错误的地方还望大牛指出,虚心学习。
浏览器客户端合法性检测
结合了其他检测技术的一个demo