大多数 IT 安全专业人员现在都非常清楚恶意机器人以及它们对任何在线业务所构成的持续威胁。因此,对反机器人软件的需求正在迅速增加。高效的机器人程序保护解决方案必须能够准确地区分不良机器人程序、良好机器人程序和人类,最好是实时进行。为了确定访问者是人还是机器人,我们可以从服务器端和客户端、浏览器或移动应用程序中收集信息。
在下文中,我们将演示为什么完全依赖服务器端检测的解决方案对某些类型的机器人无能为力,以及为什么必须由客户端信号完成分析才能真正有效地保护机器人。
服务器端指纹识别基本机器人
服务器端检测通常基于以下信息:
HTTP指纹:由浏览器发送的HTTP头组成的指纹,例如用户代理或支持的压缩算法。
TCP 指纹:TCP 指纹利用 TCP 堆栈中的差异(例如数据包的顺序)来确定发送请求的浏览器或设备的性质。
TLS 指纹:这些指纹使用一组受支持的TLS 密码套件来识别发出请求的设备和软件(例如移动应用程序)的性质。
服务器端行为特征:请求的数量、频率、是否存在浏览模式,可以用来判断用户是否为人。
这种服务器端检测是必要的主要措施,但还不够。
服务器端机器人检测的局限性是什么?
面对最新一代的机器人程序,仅具有服务器端检测功能的安全解决方案很快就会遇到其局限性。这是因为这些高级机器人使用与人类用户完全相同的浏览器——Chrome、Firefox、Safari——或像 Headless Chrome 这样的无头浏览器。
与不能执行 JavaScript 的基本机器人不同,这些高级机器人具有一致的 HTTP、TCP 和 TLS 指纹。
此外,无论何时存在小的不一致,例如非人类用户代理,都可以通过向机器人添加几行代码或使用开源检测框架来伪造一致的指纹来轻松修复(我们将对此进行更详细的讨论以下)。
如果您只做服务器端检测,那么您对这些机器人程序完全视而不见。您唯一的机会是依靠服务器端的行为特征,并等待机器人触发您的请求量阈值,然后您才能阻止它们。
这种方法总是会错过使用代理频繁更改其 IP 地址的机器人。即使他们不这样做,在您识别并阻止它们时,针对客户关键接触点(例如您的登录页面)的机器人可能已经造成了很多伤害。这就是为什么真正有效的机器人检测解决方案必须将服务器端检测与客户端检测相结合的原因。
客户端机器人检测功能:
客户端(浏览器内)跟踪可以记录和分析有关用户设备和浏览器发出请求的各种低级事实,以及行为信号。
例如:
浏览器跟踪:功能存在、js 挑战……
应用跟踪:相机版本、屏幕分辨率、触摸点数量……
设备跟踪:CPU 内核数、设备内存、GPU……
用户事件跟踪:鼠标移动和触摸事件……
这些客户端信号对于检测最先进的机器人程序至关重要,即使它们伪造指纹以绕过不太复杂的安全系统也是如此。但是不要相信我们的话:让我们通过放大一个特定的用例和一种客户端检测方法,向您展示当您不收集任何客户端信号时会发生什么。
用例:修改指纹以避免检测的高级无头 Chrome 机器人。
在此用例中,恶意行为者试图使用数千个基于 Headless Chrome 和Puppeteer 的机器人进行撞库攻击。
默认情况下,Headless Chrome 可以通过其用户代理在服务器端被识别:
Mozilla/5.0 (X11; Linux x86_64)
AppleWebKit/537.36 (KHTML, like Gecko)
HeadlessChrome/79.0.3945.88
Safari/537.36
但是,流行的开源库(例如Puppeteer extra)使开发人员能够擦除这些明显的检测信号。
Puppeteer extra 库为 Puppeteer 检测框架添加了更多功能。得益于其隐身插件,黑客可以轻松修改其 Headless Chrome 机器人的指纹。仅此一项就足以绕过大多数现有的机器人检测系统。
默认情况下,Puppeteer extra 将更改机器人的用户代理,以便它们与人类访问者保持一致,并删除传统上用于检测 Headless Chrome 的属性,例如navigator.webdriver 。
该库还使机器人开发人员能够伪造其他几个属性,例如插件列表、可用编解码器或 GPU。
如果您想自己尝试,可以使用以下代码轻松启动基于 Puppeteer 隐身的爬虫。与传统的 Puppeteer 程序相比,主要区别在于您不导入puppeteer,而是导入 puppeteer-extra:
const puppeteer = require('puppeteer-extra')
// 启用带有所有规避的隐身插件
puppeteer.use(require('puppeteer-extra-plugin-stealth')());
(async () => {
// 以无头模式启动浏览器并设置页面。const
browser = await puppeteer.launch({
headless: true
})
const page = await browser.newPage()
// 导航到将执行测试的页面。
const url = “https://你的网站……”;
等待 page.goto(url)
等待 browser.close()
})()
如果您现在验证机器人的用户代理,您可以看到它已成为合法用户代理:
const userAgent = await page.evaluate(() => {
return navigator.userAgent;
})
console.log(userAgent)
// Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome /80.0.3987.0 野生动物园/537.36
Headless Chrome 有一个等于 true 的navigator.webdriver属性。当我们在 Puppeteer stealth 中验证这个值时,我们可以看到navigator.webdriver不再出现在 bot 的指纹中,这使得这种技术无法检测到它。
const webdriver = await page.evaluate(() => {
return navigator.webdriver;
})
console.log(webdriver)
// undefined
由于用户代理和 HTTP 标头都与人类用户相同,因此简单的服务器端 HTTP 指纹识别不足以将此访问者识别为机器人。如果您的 bot 保护解决方案完全依赖服务器端检测,您唯一的希望是它迟早会显示可疑行为。
另一方面,由于先进的客户端检测,像 DataDome 这样的解决方案可以在他们第一次请求时识别这些高级机器人,即使它们是故意设计来避免检测的。
例如,Puppeteer stealth 用来绕过检测的技术之一是覆盖 canPlayType函数,该函数用于测试音频和视频编解码器的存在。
但是,这样做会留下痕迹。实际上,我们可以通过执行以下代码来测试 canPlayType 函数是否已被覆盖。
在Puppeteer stealth plugin 2.4.5版本中,运行如下代码,可获得:
const canPlayTypeTs = await page.evaluate(() => {
var audioElt = document.createElement(“audio”);
return audioElt.canPlayType.toString();
})
console.log(canPlayTypeTs)
// 'function () { [本机代码] }'
但是,对于人类使用的合法 Chrome 浏览器,您获得了:
'函数 canPlayType() { [本地代码] }'
结论:第一个用户是一个机器人。