什么是canvas指纹识别以及如何绕过
Canvas 指纹识别是阻止网络抓取工具的最智能和最受欢迎的测试之一。让我们了解它是如何工作的以及如何绕过它!
什么是canvas指纹识别?
令人惊讶的是,您在连接到网站时可能会共享大量信息:您的操作系统、屏幕分辨率、时区等。尽管这些看起来很普通且无关紧要,但它们会创建一个独特的指纹(或配置文件)组合起来识别您的身份高达99.99%+ 的准确率。
Canvas 指纹识别是浏览器指纹识别中的一个过程,大约 5.5%的最受欢迎的互联网网站使用它发起图形挑战以收集许多数据点,使用它的一些原因是:
- 更好的安全性:识别机器人以防止恶意攻击。如果网站检测到客户行为有任何异常,它会迅速采取行动并限制访问。
- 个性化用户体验:消费者声称他们更倾向于选择提供个性化体验的网站。canvas指纹有助于根据以前的行为向用户提供建议的内容。
那么这个过程是如何进行的呢?
canvas指纹如何工作?
资料来源:Pixabay
在每次访问网页时,特定的 JavaScript 代码会指示浏览器在 HTML5 canvas上绘制具有随机元素和背景的图形。此图像生成创建唯一用户指纹所需的信息。例如,示例canvas指纹识别脚本会生成如下图所示的图像。
不同的计算机可能会对上面的canvas图像进行不同的渲染。这是因为操作系统、图像处理引擎和压缩级别等因素因设备而异。例如,配备最先进 GPU 和高屏幕分辨率的现代设备使用提示和抗锯齿等滤镜来改善图像的外观。
即使您在不同的设备上看到相同的生成图像,计算机也可以分辨出差异。
以下是网站生成canvas指纹所需步骤的快速分解:
- 用户访问网站。
- 该站点触发其基于 JS 的canvas指纹识别脚本。
- HTML 在浏览器中生成隐藏到眼睛的图像。
- 该脚本根据客户端的操作系统、浏览器和 GPU 创建图像的 Base64 表示。
- 然后它计算表示的散列。
您可以使用BrowserLeaks查看用户的指纹:
上图的一个关键元素是 PNG 哈希值,它表示图像数据。更重要的是,我们需要了解散列(这个值是如何生成的)来绕过canvas指纹识别。
散列是将数据转换成固定长度的字符串而不丢失唯一性。让我们看看为什么使用它以及接下来它是如何工作的。
散列如何在 Canvas 指纹识别中工作?
canvas图像生成的数据通常很大且难以存储,因此这就是散列函数的用武之地。它采用长数据集并将其缩减为标准化数据,称为散列。
哈希用于生成canvas指纹,因为它对相同的输入产生相同的结果。这是一个例子:
SHA-256 哈希函数中的句子“canvas fingerprinting”将产生以下结果:
fb2b4c2da0dfaa3bcbf89caf59389d4604739a0490137c970eb55c44c1105f89
然而,如果我们运行相同的句子,但在字母c
‘ 之前有一个空格,我们将得到一个新的散列:
620fe0d249aa4d17524ae4c3b3332a8be2913a750bb151bf225794cdcb5ba4c1
如何绕过canvas指纹识别?
理想情况下,您可以通过禁用canvas API进行抓取而不会被阻止。但是,这需要进行一些权衡。例如,一些网站依靠canvas来显示内容。因此,如果没有canvas API,某些功能可能无法使用。
另一种方法是禁用 JavaScript。但是,大多数网站都依赖它来显示内容。
我们的目标不同。我们想要一个网站可以接受的指纹。那么,您如何着手创建“正确”的呢?两种流行的方法是使用无头浏览器和启用反拉票扩展。
请记住:任何细微的变化都会产生独一无二的指纹。因此,通过直接更改canvas图像数据或图像,我们生成了一个新图像。让我们使用 Puppeteer 看看它的实际效果:
先决条件
要学习本教程,您需要:
- 从官方网站安装的Node和 npm。
- puppeteer。
npm install puppeteer
注意:使用其他语言和无头浏览器可以实现相同的结果。
方法#1:使用 Base Puppeteer 绕过 Canvas 指纹识别
如何模拟指纹?您需要确定负责canvas指纹识别的脚本,并将其替换为经过改编的版本。
假设我们需要绘制一个 209 像素 x 25 像素的图像,然后继续为这种情况生成假指纹。
首先,导入 Puppeteer 并启动一个新的浏览器实例。
// Import the Puppeteer library const puppeteer = require('puppeteer'); (async () => { // Launch a new browser instance and create a new page const browser = await puppeteer.launch({ headless: false }); const page = await browser.newPage();
接下来,定义一个函数来修改toDataURL()
HTMLCanvasElement 原型的方法。此函数收集原始toDataURL()
方法,检查网站是否绘制具有预先规定尺寸的图像,并返回假指纹。此外,应在创建新文档时和执行任何脚本之前调用它。
await page.evaluateOnNewDocument(() => { const mainFunction = HTMLCanvasElement.prototype.toDataURL; HTMLCanvasElement.prototype.toDataURL = function (type) { // check if this is a fingerprint attempt if (type === 'image/png' && this.width === 209 && this.height === 25) { // return fake fingerprint return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAAAeCAAAAABiES/iAAACeElEQVRYw+2YzUtUURjGf47OmDPh5AyFomUiEeEmyghXtWsh4dcswlYV2KYWfZh/QRBUVLhTCCJXEgmKUCIkFhJREARBkbkyKBlTRmUC82lxZ7z3TjM4whwXwz2ry3vO87znx33Pey4XFfHAg/PgPDgPzoPz4Dy4rFIKscSkAfmnsUY+iTfXFhxue4Zm4QpfaKbg8k+EsZNsGG6iNVzRMrkZeRPmjp6eCgcae5f+3wJIgtWLldG+DUnfzoail1etaVsEa1f2lUqw2hPd3T7nCrkMtlkQ24YDwP8+FZkI+gY3uq2cTcu54GIA/dJCDUAnSE4RdAESdALUxZ0hl4E5OMs49iE528E5a+cj5YFhDVI3vLA2c4K+zLXpvR37tNRDs3STg1OJqXqQSwS14wlJUD+VeHWAW86Qy8BwQ5Ek/WK/JBgqC72UTvJakmY5lAvurTRPSDrMmKRRcIvgeUo2KmmEI86Qy8DwmVu/ezQIBCSBLzwjKZhujv5cZZmUNkAq57ekRXCLYDG12pre5Qy5DAzDXbPfIOB/JqmCzNafCZd+dMA5RfZxdsBlNTAMF+FJfD2eSvSI0iGpmXe5GnbG3qyyHAO3yCZxlGV2uBLWDcJVMZKc7UrnfIBvQI+pHpxbS34ZaNkK7gYN0yvTDSCXyCZxNJTscFFe/DUH1w3QvpnzPiUPdTXfsvxZDdBGmeQU2SQd9lWQHS5m9J6Ln4/suZCwc96D25qM1formq5/3ApOX1uDkZ7P7JXkENkkK5eqQm3flRtuvitSYgCucKOf0zv01bazcG3Tyz8GKukvSjjrlB3/U5Rw42dqAo29yypKOO8figeX1/gH+zX9JqfOeUwAAAAASUVORK5CYII='; } // otherwise, just use the main function return mainFunction.apply(this, arguments); };
因此,您的完整代码应如下所示:
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch({ headless: false }); const page = await browser.newPage(); await page.evaluateOnNewDocument(() => { const mainFunction = HTMLCanvasElement.prototype.toDataURL; HTMLCanvasElement.prototype.toDataURL = function (type) { // check if this is a fingerprint attempt if (type === 'image/png' && this.width === 209 && this.height === 25) { // return fake fingerprint return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAAAeCAAAAABiES/iAAACeElEQVRYw+2YzUtUURjGf47OmDPh5AyFomUiEeEmyghXtWsh4dcswlYV2KYWfZh/QRBUVLhTCCJXEgmKUCIkFhJREARBkbkyKBlTRmUC82lxZ7z3TjM4whwXwz2ry3vO87znx33Pey4XFfHAg/PgPDgPzoPz4Dy4rFIKscSkAfmnsUY+iTfXFhxue4Zm4QpfaKbg8k+EsZNsGG6iNVzRMrkZeRPmjp6eCgcae5f+3wJIgtWLldG+DUnfzoail1etaVsEa1f2lUqw2hPd3T7nCrkMtlkQ24YDwP8+FZkI+gY3uq2cTcu54GIA/dJCDUAnSE4RdAESdALUxZ0hl4E5OMs49iE528E5a+cj5YFhDVI3vLA2c4K+zLXpvR37tNRDs3STg1OJqXqQSwS14wlJUD+VeHWAW86Qy8BwQ5Ek/WK/JBgqC72UTvJakmY5lAvurTRPSDrMmKRRcIvgeUo2KmmEI86Qy8DwmVu/ezQIBCSBLzwjKZhujv5cZZmUNkAq57ekRXCLYDG12pre5Qy5DAzDXbPfIOB/JqmCzNafCZd+dMA5RfZxdsBlNTAMF+FJfD2eSvSI0iGpmXe5GnbG3qyyHAO3yCZxlGV2uBLWDcJVMZKc7UrnfIBvQI+pHpxbS34ZaNkK7gYN0yvTDSCXyCZxNJTscFFe/DUH1w3QvpnzPiUPdTXfsvxZDdBGmeQU2SQd9lWQHS5m9J6Ln4/suZCwc96D25qM1formq5/3ApOX1uDkZ7P7JXkENkkK5eqQm3flRtuvitSYgCucKOf0zv01bazcG3Tyz8GKukvSjjrlB3/U5Rw42dqAo29yypKOO8figeX1/gH+zX9JqfOeUwAAAAASUVORK5CYII='; } // otherwise, just use the main function return mainFunction.apply(this, arguments); }; }); await page.goto('https://browserleaks.com/canvas'); })();
通常,任何图像都会产生新的指纹。但是,如果您的次数太少,您可能仍会被阻止。因此,更安全的做法是模仿合法浏览器的图像。您可以复制一个或使用您选择的图像编辑器自己创建。
方法 #2:启用反拉票扩展
如果您无法模仿实际浏览器的图像,还有另一种方法:使用无头浏览器下载并启用反拉票扩展。
Chrome 的 Canvas Fingerprint Defender是一个不错的选择,因为它会生成接近真实用户指纹的随机假指纹。让我们做一个快速测试,先不使用扩展名:
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch({ headless: false }); const page = await browser.newPage(); await page.goto('https://browserleaks.com/canvas'); })();
我们的基础 Puppeteer 脚本会生成一个具有 99% 唯一性的指纹,如下图所示。虽然此指纹非常独特,但您仍然可以与 3083 Puppeteer 生成的指纹一起被阻止,这些指纹可能已被列为机器人阻止列表。
但是,当我们添加扩展时会发生什么?
在浏览器中下载扩展程序,然后前往代码编辑器并添加扩展程序的路径。
const puppeteer = require('puppeteer'); (async () => { // Path to extension folder--- replace with your extension path const pathToExtension = 'C:\Users\Chesc\AppData\Local\Google\Chrome\User Data\Default\Extensions\lanfdkkpgfjfdikkncbnojekcppdebfpconst puppeteer = require('puppeteer'); (async () => { // Path to extension folder--- replace with your extension path const pathToExtension = 'C:\Users\Chesc\AppData\Local\Google\Chrome\User Data\Default\Extensions\lanfdkkpgfjfdikkncbnojekcppdebfp\0.2.0_0';.2.0_0';
注意: Chrome 插件通常存储在浏览器中。要在您的本地计算机上访问它,请从您的浏览器获取扩展程序的 ID,然后导航到 Chrome 在您的设备上存储它们的文件夹并找到具有相同 ID 的文件夹。
然后启用扩展。
try { console.log('==>Open Browser'); const browser = await puppeteer.launch({ // Disable headless mode headless: false, // Pass the options to install the extension args: [ `--disable-extensions-except=${pathToExtension}`, `--load-extension=${pathToExtension}`, ] });
我们完整的代码应该是这样的:
const puppeteer = require('puppeteer'); (async () => { // Path to extension folder--- replace with your extension path const pathToExtension = 'C:\Users\Chesc\AppData\Local\Google\Chrome\User Data\Default\Extensions\lanfdkkpgfjfdikkncbnojekcppdebfpconst puppeteer = require('puppeteer'); (async () => { // Path to extension folder--- replace with your extension path const pathToExtension = 'C:\Users\Chesc\AppData\Local\Google\Chrome\User Data\Default\Extensions\lanfdkkpgfjfdikkncbnojekcppdebfp\0.2.0_0'; try { console.log('==>Open Browser'); const browser = await puppeteer.launch({ // Disable headless mode headless: false, // Pass the options to install the extension args: [ `--disable-extensions-except=${pathToExtension}`, `--load-extension=${pathToExtension}`, ] }); const page = await browser.newPage(); // Navigate to browser leaks await page.goto('https://browserleaks.com/canvas'); } catch (err) { console.error(err); } })();.2.0_0'; try { console.log('==>Open Browser'); const browser = await puppeteer.launch({ // Disable headless mode headless: false, // Pass the options to install the extension args: [ `--disable-extensions-except=${pathToExtension}`, `--load-extension=${pathToExtension}`, ] }); const page = await browser.newPage(); // Navigate to browser leaks await page.goto('https://browserleaks.com/canvas'); } catch (err) { console.error(err); } })();
下面是我们的结果:
这就是 Defender 在幕后随机化您的指纹所做的事情:
const getImageData = CanvasRenderingContext2D.prototype.getImageData; const noisify = function (canvas, context) { const width = canvas.width, height = canvas.height; // ... noisify imageData ... context.putImageData(imageData, 0, 0); }; Object.defineProperty(CanvasRenderingContext2D.prototype, "getImageData", { value: function () { noisify(this.canvas, this); return getImageData.apply(this, arguments); }, }); // Something similar for HTMLCanvasElement.toBlob and HTMLCanvasElement.toDataURL
Canvas 指纹识别使用三个函数检索信息:toBlob()
、getImageData()
和toDataURL()
。但是,该脚本将 JavaScript 注入到文档中以监视和更改所包含程序的行为。换句话说,函数在代码中被重新定义。因此,当网站调用它们创建指纹时,它会收到随机生成的数据。
重新定义toBlob()
和toDataURL()
函数会更改 HTML canvas 元素数据,同时getImageData()
更改渲染界面。
结论
Canvas 指纹识别对于网站来说很容易实现,一些大规模使用的反机器人系统(如Cloudflare和DataDome)也附带了它,但爬虫要绕过它具有挑战性。
以下是您今天学到的所有内容的快速回顾:
- 什么是canvas指纹识别及其工作原理。
- 如何使用基础 Puppeteer 绕过它。
- 如何创建启用反拉票扩展的假指纹。
虽然这些方法在某些情况下可以为您带来成功,但它们仍然存在一定的风险和局限性。为了节省您自己的时间和精力并确保您毫无障碍地实现您的抓取目标。