目录
接上篇关于TLS握手指纹 JA3的一些发现,本文记录在JA3的php实践。
事后诸葛,这个是蛋疼的事情。如果你想应用ja3,建议直接安装nginx的ja3模块等工具。为啥怎么说,你再看看就知道了。
分析
JA3是由TLS握手数据计算来的,无论是在nginx+php-fpm+php或者nginx+workmanphp的web架构,TLS流量都止于nginx。此时在php上能获取的TLS的信息则全部取决于nginx默认的ngx_http_ssl_module提供了什么信息,而nginx的ja3模块则补充了ja3的信息。
那么,我们要做什么呢?而用php能实现多少呢?
- 一个流量嗅探器,替代ngixn或者前置于nginx。这样我们能取得握手数据,从而获得JA3
- 把JA3传回请求,脱离内容的指纹好像作用不大
替代nginx
比较合理的方案是替代nginx,效率、数据传递和数据处理都会方便。不过捏,我没搞定。
前置的嗅探器
前置于nginx流量嗅探器,简单的说就是流量转发,在转发过程中提取相关数据。灵感是来着于之前的测试:workerman TCP代理限速。流量路径如下:
brower => ja3Catch => nginx(https=>http) => php
数据传递
在nginx中,可以接收到TLS数据,解密后提取http数据,随后传递给php。解密前取得的JA3可以直接在传给php前塞入HTTP数据包。而现在只是个流量嗅探器,只能处理握手部分的明文部分,无法修改密文,怎么才能传递数据呢?或者说是建立JA3数据和后续链接的联系呢?
我先手尝试了3个思路:
-
ssl_session_id
以ssl_session_id做识别,该方法可行,但有缺点。如关于TLS握手指纹 JA3的一些发现中记录的,ssl_session_id会变,并且首次访问为空。实践访问时,需要做一次刷新。在浏览器上没问题,但如果是curl,只能使用curl -L了,也就是说不选 -L,方案就取不到JA3。
-
server_name
以server_name做识别,该方法也可行,但有缺点。每次查询都得分配一个域名来作为识别标志,如果要保持链接地址不变,则要做2次跳转:
ja3.com => rank.ja3.com => ja3.com
通用curl也有和方案一一样的问题。
-
remote_port
$_SERVER[‘REMOTE_PORT’]是用户机器上连接到 Web 服务器所使用的端口号。
而
端口号:用来辨别本地通讯进程,一个本地的进程在通讯时均会占用一个端口号,不同的进程端口号不同,因此在通讯前必须要分配一个没有被访问的端口号。
作为三元组的关键信息,在我一开始就想作为标志,奈何在测试中因为输出错端口号信息,以至于以为$_SERVER[‘REMOTE_PORT’]是php-cgi 和nginx通讯的端口。寻寻觅觅那人却在灯火阑珊处。
demo
wkm_ja3er demo
源码
Xxx-Bin/php-ja3
收获
- 二进制数据处理
在之前字体加密中阅读的php-font-lib BinaryStream 被魔改下就用上了
- 加深对TCP/TLS 协议的认识
之前只是大概看过,没有深入学习。研究一个cliet hello ,还是顺带看了不少东西的
- Wireshark 基本会了
之前研究ping、dns,有针对性抓取过对应的包,相对来说,UDP的单包还是比较好理解和操作的。到了TCP和TLS,几个握手和复杂的协议,单看字段就看瞎了。
- php-socket
在抓包和看workman源码时,我才慢慢的理解到。
- TCP的3次握手发生在什么阶段。
客户端的socket_connect和服务端的socket_accept。平时写完就完事,才不去管到底发生了什么。
- 客户端 TLS 握手呢
stream_socket_enable_crypto 一个函数就搞定了。详细可看:workman-tcp-doSslHandshake。当然,调用了TLS握手数据就离你而去,JA3就算不出来咯。
补充
-
当我实践部署到服务器上后,在本地会出现的有sesseion_ticket而导致ja3值改变的情况则不见了。困惑。
-
手里几个系统的情况
- win
edge、chrome 一样
Chromium、微信浏览器一样
ie、firefox、curl和以上均不同
- iphone
单个手机全部浏览器一致,但和另外一个不同型号iphone不同,也可能时系统版本问题
- Android
单个手机每个浏览器都不一样
-
如果前面还套了cdn,这个方案无效,至少在cloudfalre上是这样的。
相反在cloudfalre中购买机器人服务的企业用户可以使用该服务,详情见cloudfalre JA3指纹