前言
前两天和隔壁做风控的同学聊天,据说他们经常使用浏览器指纹来识别和标记爬虫(当然具体的细节是不能透露的),联想到我们最近也经常遇到被风控的情况,于是就花了点时间研究下浏览器指纹相关的知识。
需要明确的是,“隐藏浏览器指纹”和“隐藏Webdriver/Selenium/Puppeteer”、“匿名浏览”都不是同一个问题。“隐藏Webdriver/Selenium/Puppeteer”的目的是告诉服务端自己不是自动化爬虫(这个似乎可以尝试用 stealth.min.js 来做);“匿名浏览”的目的是不让浏览记录在本地留下痕迹(这个可以用浏览器的匿名模式来实现);“隐藏浏览器指纹”是为了不让服务端追踪“现在的我和之前的我是同一个我”(这个是我们这次想解决的问题)。
浏览器指纹一般是通过 Javascript 能提取或计算得到的一些具有一定稳定性和独特性的参数。风控的同学将这些参数做一定取舍和组合,就能得到一台浏览器的大致标识。在没有强制登陆的情况下,有了这个标识,风控的同学可以做反爬、广告的同学可以做推荐、数据的同学可以算uv等等,用处还是不小的。
常见的浏览器指纹会提取如下东西:
- UserAgent和平台信息
- 浏览器加载的字体信息
- 声卡指纹
- Canvas指纹
- 显示器分辨率
- 语言、地区时区信息
- CPU核数和可用内存信息
- 本地存储、Cookie等信息
- 浏览器安装的插件信息
- 科学计算的指纹信息
- IP和代理信息
可以看出来,这里有的指标稳定性比较好,比如UserAgent、分辨率、地区、语言、字体信息等,但是这些指标通常区分度不是很好。因此我们通常更喜欢用一些硬件指纹信息来进行区分。当然,为了衡量指标的这种“独特性”,Peter Eckersley 提出了用一个指标对设备唯一指纹引入的熵来衡量指标的这种特性,下面是我的设备在 coveryyourtracks 中跑出来的一些常见指标的效果:
代码语言:javascript复制USER AGENT
Bits of identifying information: 9.04
One in x browsers have this value: 524.71
HTTP_ACCEPT HEADERS
Bits of identifying information: 8.96
One in x browsers have this value: 496.75
BROWSER PLUGIN DETAILS
Bits of identifying information: 3.25
One in x browsers have this value: 9.54
TIME ZONE OFFSET
Bits of identifying information: 5.26
One in x browsers have this value: 38.38
TIME ZONE
Bits of identifying information: 6.83
One in x browsers have this value: 114.13
SCREEN SIZE AND COLOR DEPTH
Bits of identifying information: 6.28
One in x browsers have this value: 77.57
SYSTEM FONTS
Bits of identifying information: 3.92
One in x browsers have this value: 15.12
ARE COOKIES ENABLED?
Bits of identifying information: 0.13
One in x browsers have this value: 1.09
LIMITED SUPERCOOKIE TEST
Bits of identifying information: 1.41
One in x browsers have this value: 2.67
HASH OF CANVAS FINGERPRINT
Bits of identifying information: 9.73
One in x browsers have this value: 847.61
HASH OF WEBGL FINGERPRINT
Bits of identifying information: 9.75
One in x browsers have this value: 862.69
WEBGL VENDOR & RENDERER
Bits of identifying information: 8.5
One in x browsers have this value: 362.9
DNT HEADER ENABLED?
Bits of identifying information: 0.95
One in x browsers have this value: 1.93
LANGUAGE
Bits of identifying information: 7.47
One in x browsers have this value: 176.69
PLATFORM
Bits of identifying information: 3.11
One in x browsers have this value: 8.62
TOUCH SUPPORT
Bits of identifying information: 0.76
One in x browsers have this value: 1.7
AD BLOCKER USED
Bits of identifying information: 0.15
One in x browsers have this value: 1.11
AUDIOCONTEXT FINGERPRINT
Bits of identifying information: 5.92
One in x browsers have this value: 60.59
CPU CLASS
Bits of identifying information: 0.12
One in x browsers have this value: 1.09
HARDWARE CONCURRENCY
Bits of identifying information: 1.93
One in x browsers have this value: 3.82
DEVICE MEMORY (GB)
Bits of identifying information: 2.31
One in x browsers have this value: 4.96
其中,“Bits of identifying information” 表示这个指标对我的浏览器引入的唯一性的位数,这个位数越大,越表明这个指标更能区分我的浏览器和其他的浏览器;“One in x browsers have this value” 表示平均多少个浏览器的这个指标和我的浏览器的这个指标一样,这个数越大,越表明这个指标的区分度好。
显然,从这里看,区分度最高的指标就是 "UserAgent" , "WebGL指纹" 和 "Canvas 指纹" 。那这些指纹是怎么 work 的呢?例如Canvas指纹,其实就是由于Web 浏览器使用的图像处理引擎、图像导出选项、压缩级别、操作系统的字体,抗锯齿和亚像素渲染算法等的不同,导致画出来的图片在像素级别存在的微小但稳定的差距。这样我们就可以拿这个生成图片的校验和或算一个Hash作为他的指纹。
指纹检测
目前业内较为知名的浏览器指纹检测网站大概有下面这三个:
- https://fingerprintjs.github.io/fingerprintjs/
- http://f.vision/
- https://coveryourtracks.eff.org/
第一个是最常见的浏览器指纹生成项目,之前某手用过一段时间,后来好像不用了,可以作为入门项目来看。
第二个是一个进阶的在线检测指纹的网站,检测点更全,也有一定的反伪造能力。
第三个是 Peter Eckersley 实验用的检测指纹的网站,教育意义更明显,也有一定的反伪造能力。
有矛就有盾,作为(爬虫)普通用户,我们显然不想让服务端跟踪我们的操作路径,但又不想用 Tor 浏览器。因此我们就要想一些对付这些指纹检测的办法。下面就是我做的一些尝试。
Fingerpintjs指纹
指纹生成算法
首先我们先看下 fingerprintjs 检测 Canvas 指纹的核心代码,作为我们首先需要绕过的目标:
代码语言:javascript复制function makeTextImage(canvas, context) {
// Resizing the canvas cleans it
canvas.width = 240;
canvas.height = 60;
context.textBaseline = 'alphabetic';
context.fillStyle = '#f60';
context.fillRect(100, 1, 62, 20);
context.fillStyle = '#069';
// It's important to use explicit built-in fonts in order to exclude the affect of font preferences
// (there is a separate entropy source for them).
context.font = '11pt "Times New Roman"';
// The choice of emojis has a gigantic impact on rendering performance (especially in FF).
// Some newer emojis cause it to slow down 50-200 times.
// There must be no text to the right of the emoji, see https://github.com/fingerprintjs/fingerprintjs/issues/574
// A bare emoji shouldn't be used because the canvas will change depending on the script encoding:
// https://github.com/fingerprintjs/fingerprintjs/issues/66
// Escape sequence shouldn't be used too because Terser will turn it into a bare unicode.
var printedText = "Cwm fjordbank gly " String.fromCharCode(55357, 56835) /*