Post

浏览器的指纹ja3的研究,以及一些绕过办法

浏览器的指纹ja3的研究,以及一些绕过办法

前言

在爬虫的过程中经常会遇到一些反爬虫机制,最常见的就是通过User-Agent来进行简单的反爬虫,此类简单的反爬直接通过改header即可,但是现在越来越多的网站开始使用更复杂的反爬虫机制,比如通过浏览器指纹来识别用户。 通过浏览器将cookie和浏览器指纹进行绑定,哪怕传递完全一样的cookie header 也会被识别为不同的用户,从而达到反爬虫的目的。 本文将介绍浏览器指纹的概念、常见的浏览器指纹技术以及一些绕过浏览器指纹的方法。

什么是浏览器指纹

我们常说的浏览器指纹比较宽泛,可能有用户的硬件信息,操作系统信息,浏览器信息,浏览器插件信息,字体信息,分辨率信息等。 这里以比较常见的tls指纹:ja3为例。这是一种通过TLS握手包中的一些字段来生成一个唯一的指纹,也是cloudflare的常用的人机校验算法。

计算 ja3

通过tls握手包中的五个字段:ClientHello 的版本、可接受的加密算法、扩展列表中的每一个 type 值、支持的椭圆曲线和支持的椭圆曲线格式。用,来分隔各个字段、用使用-来分隔各个字段中的各个值(十进制),将这些值串联在一起之后,计算 MD5,就是一个 ja3 了。 注意,如果没有某个字段,则这些字段的值为空(连接用的逗号别忘了)。 查看自己浏览器的ja3的网站;

  • https://tls.browserleaks.com/json

返回字段类似于:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
  "ja3_hash": "8d12438070134dbf666487af79773122",
  "ja3_text": "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,5-35-0-17613-27-16-51-45-18-13-65037-23-43-11-10-65281,4588-29-23-24,0",
  "ja3n_hash": "8e19337e7524d2573be54efb2b0784c9",
  "ja3n_text": "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-5-10-11-13-16-18-23-27-35-43-45-51-17613-65037-65281,4588-29-23-24,0",
  "ja4": "t13d1516h2_8daaf6152771_d8a2da3f94cd",
  "ja4_r": "t13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601",
  "ja4_o": "t13d1516h2_acb858a92679_06588b6a94ab",
  "ja4_ro": "t13d1516h2_1301,1302,1303,c02b,c02f,c02c,c030,cca9,cca8,c013,c014,009c,009d,002f,0035_0005,0023,0000,44cd,001b,0010,0033,002d,0012,000d,fe0d,0017,002b,000b,000a,ff01_0403,0804,0401,0503,0805,0501,0806,0601",
  "akamai_hash": "52d84b11737d980aef856699f885ca86",
  "akamai_text": "1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p"
}

其中的ja3_text字段即为ja3的计算结果。ja3_hash 是对ja3_text做md5计算后的结果。

由于现代浏览器更新非常快,哪怕是同样的一个电脑,其中的浏览器也会偶尔发生变化,所以ja3的值也会时不时发生变化。在做爬虫时需要注意这一点。

伪造 ja3

理论上,伪造ja3是非常困难的,因为现代的语言的tls套件都非常的底层,我们在开发爬虫时也难以直接操作tls握手包。如果要硬操作可能要手写tls握手轮子,这显然不现实。

好在还是有一些语言中的网络协议栈允许我们伪造ja3,比如python的requests库,go的http库等。

市面上的有一些python的魔改库:

1
2
tls_client : https://pypi.org/project/tls-client/
curl_cffi: https://pypi.org/project/curl-cffi/

go语言的库:这个看代码是自己实现了 TLS 握手,实在是令人佩服。为了兼容 HTTP2 以及各种复杂的 TLS 参数,这个库目前还在艰难地维护当中,向开发者致敬。

1
CycleTLS: https://github.com/Danny-Dasilva/CycleTLS

这边不赘述如何使用,可以参考各个库的文档。

我个人的方案 抛砖引玉

因为我这边的爬虫主要是以java项目为主,java的tls套件比较底层,难以直接操作tls握手包,所以我这边采用了一个变通的方案。 通过代理的形式进行ja3的伪造。

即在调用接口时添加一个中间层,指定ja3指纹后进行转发。 调用链路为:

爬虫程序 -> ja3代理 -> http代理(ip池) -> 目标网站

一开始我使用的代理为直接指定了socket代理的形式,但是这样的问题是需要自定义证书,这样需要每个爬虫发起方都需要信任此证书。改动面比较大。

后面选用了侵入代码的方式,更改调用方案,通过特殊的header指定url进行调用。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Direct request shows real JA3 fingerprint
$ curl -s https://tls.peet.ws/api/all | jq -r '.tls.ja3'
"771,4866-4867-4865-4868-49196-49200-52393-52392-49325-49195-49199-49324-49187-49191-49162-49172-49161-49171-157-49309-156-49308-61-60-53-47-159-52394-49311-158-49310-107-103-57-51-255,0-11-10-13172-16-22-23-49-13-43-45-51-21,29-23-30-25-24,0-1-2"

# Proxied request shows spoofed JA3 fingerprint
$ curl http://localhost:8080 -H "X-Target-URL: https://tls.peet.ws/api/all" | jq -r '.tls.ja3'
"771,52393-52392-52244-52243-49195-49199-49196-49200-49171-49172-156-157-47-53-10,65281-0-23-35-13-5-18-16-30032-11-10,29-23-24,0"

# With custom JA3 and User-Agent
$ curl http://localhost:8080
    -H "X-Target-URL: https://tls.peet.ws/api/all" \
    -H "X-JA3-Fingerprint: 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-21,29-23-24,0" \
    -H "X-User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36" | jq -r '.tls.ja3'
"771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-21,29-23-24,0"

# With custom proxy
curl http://localhost:8080 -H "X-Target-URL: https://ifconfig.me/ip" -H "X-Proxy: http://login:pass@ip:port"

我的项目是根据一个现成的项目:https://github.com/cuamckuu/JA3ProxySpoofer 改的,感谢原作者。此项目是对上面的CycleTLS的包装。

由于原作者已经不更新了,我手动修改了其中的一些bug,添加了一些功能,升级了CycleTLS依赖版本。正如我上面所说,现代浏览器更新太快了,可能过几年ja3就变了许多,支持了更多的tls扩展,所以我们是需要保持对ja3代理的更新。如果后续此项目也不可用了欢迎给我提issues,我去更新版本。

由于我的爬虫部署在我的闲置笔记本上,系统版本是windows,我就懒得打包linux版本了,直接在windows上跑的。理论上linux也可以直接跑,通过linux的go语言打包镜像打包后,再封装成镜像直接运行即可。

启动方式:

1
ja3proxy.exe -port 8081

在调用爬虫时指定http地址为ja3proxy的地址。实际需要调用的url通过header:X-Target-URL传递;需要模拟的ja3通过header:X-JA3-Fingerprint传递;需要模拟的User-Agent通过header:X-User-Agent传递。其余的header(包括cookie)照常传递即可会被自动转发。

相关责任申明

本文仅供技术研究使用,请勿用于非法用途。作者不对因使用本文内容而产生的任何法律责任承担责任。

由于爬虫属于灰色地带,请遵守相关法律法规,尊重目标网站的robots协议。

参考资料

https://www.tr0y.wang/2020/06/28/ja3/

https://www.cnblogs.com/zichliang/p/17491838.html

This post is licensed under CC BY 4.0 by the author.