The Castle blog The Castle blog Subscribe Research · May 7, 2025 · 4 min read

用一个奇特的技巧检测并崩溃 Chromium 机器人 (机器人讨厌它!)

Detect and crash Chromium bots with one weird trick (bots hate it!)

免责声明:如果您是为了寻找机器人检测的圣杯而来,那么这可能不是。除非您的用户体验策略包括惊喜弹窗,并且您的营销策略包括阻止 Google 爬虫。

我们最近在 Chromium bug tracker 上发现了一个 bug,一段简短的 JavaScript 代码可以使无头 Chromium 浏览器崩溃,例如那些被 Puppeteer 和 Playwright 使用的浏览器。 听起来像一个梦寐以求的机器人信号,对吧? 检测机器人,崩溃它们的浏览器,并且全部来自客户端 JS,无需服务器。 如果您足够幸运,您甚至可能导致它们服务器上的内存泄漏!

也许吧。也许不是。在这篇文章中,我们将分析这个 bug,探讨如何利用它进行检测,并最终解释为什么在生产环境中使用它可能不是一个好主意。

分析 Bug 报告

Bug 追踪器不仅仅是给受挫的工程师使用的,它们也是机器人猎人的金矿。 每一个无头浏览器的怪癖或自动化 bug 都是潜在的检测信号。 如果它在 Puppeteer 中出现问题,但在 Chrome 中没有问题,那么它可能值得仔细研究。

这个 bug 非常简单。 在具有特定参数的 iframe 上调用 contentWindow.open,浏览器就会崩溃。 在 Puppeteer 和 Playwright 中都可以完全重现:

const iframe = document.createElement("iframe");
iframe.src = "data:text/html,<body></body>";
document.body.appendChild(iframe);
iframe.contentWindow.open("", "", "top=9999");

为了说明,这是一个 Playwright 机器人导航到 Hacker News,截取屏幕截图,然后引爆崩溃的例子:

import { chromium } from "playwright";
(async () => {
  const browser = await chromium.launch({ headless: false });
  const context = await browser.newContext();
  const page = await context.newPage();
  
  await page.goto('https://news.ycombinator.com');
  
  await page.waitForTimeout(1000);
  await page.screenshot({ path: 'screenshot.png' });
  
  try {
    await page.evaluate(() => {
      const iframe = document.createElement("iframe");
      iframe.src = "data:text/html,<body></body>";
      document.body.appendChild(iframe);
      iframe.contentWindow.open("", "", "top=9999");
    });
  } catch (error) {
    console.log(error);
  }
  
  await browser.close();
})();

您应该注意,try/catch 块没有任何作用。 没有抛出任何异常。 对 page.evaluate 的调用只是挂起,浏览器悄无声息地死亡。 browser.close() 永远不会被执行,这可能会随着时间的推移导致内存泄漏。

创建终极机器人检测信号?

注意问号。 让我们不要太激动。

这是 botCheckMate 的代码,我们这个不太完美的检测器:

function botCheckMate() {
	const iframe = document.createElement("iframe");
	iframe.src = "data:text/html,<body></body>";
	document.body.appendChild(iframe);
	iframe.contentWindow.open("", "", "top=9999");
	
	// After this point, if the code didn't crash, then you're human
	return false;
}
let isBot = botCheckMate();

如果您是人类,这将返回 false。 如果您是基于 Chromium 的机器人,您会崩溃,并且我们保存一个返回值! #效率至上

您可以通过在您的浏览器开发者工具中运行它来验证,它将返回 false。 如果您使用 Puppeteer 或 Playwright (带有 Chrome) 运行它,您的浏览器将会崩溃。

为什么这在生产环境中是一个糟糕的主意

虽然本文的语气显然是半开玩笑的,但有一个严肃的结论:并非所有检测信号都适合在生产中使用。 特别是这一个,附带了许多缺点,远远超过了它的新颖性。

首先,为人为用户触发弹窗很少是一个好主意。 大多数人并不期望 (或想要) 未经请求的窗口在他们面前打开。 它打破了用户的期望,中断了他们的流程,并且几乎肯定会降低用户体验。 说实话:您的 CMO 可能也不会感到兴奋。

然后是副作用的问题。 构建机器人检测系统的基本原则之一,特别是我们在 Castle 中采用的方法,是尽量减少影响。 我们更喜欢安静且不引人注意的信号,这些信号不会记录嘈杂的事件,不会使 CPU 飙升,也不会触发控制台警告。 这个检测方法? 它是数字化的喊叫。

另一个主要问题是如何将检测和响应紧密联系起来。 将两者合并起来是很诱人的,尤其是当响应如此令人满意时,但这很少是正确的方法。 良好的机器人检测意味着将检测与您采取的行动分开。 您可能想要阻止用户,对其进行影子禁令,标记其帐户以供审核,或者什么都不做。 但是一旦您崩溃了他们的浏览器,选择就已经做出了。

此外,由于整个策略都在客户端执行,因此您将失去对原本可以用于决策的大多数有用元数据的访问权限。 您无法在服务器端存储机器人签名,管理 Googlebot 或您自己的 QA 工具的允许列表,或根据威胁级别调整响应。

最后,机器人会进化。 一旦机器人作者弄清楚是什么导致崩溃,他们就会覆盖 open() 方法或清理参数。 游戏结束。 您又回到了检测军备竞赛中。 想要更深入并检测覆盖? 我们使用诸如我们的 canvas randomization article 中的技术来为您提供支持。 但是那样您就进入了一场全面的猫捉老鼠的游戏,以及随之而来的所有维护。

所以,是的,这个信号有效。 但并非没有警告,当然也不是没有代价。

结论

在纸面上,这种检测看起来令人难以抗拒。 几行 JavaScript 代码,噗,机器人浏览器消失了。 它干净、戏剧化且非常令人满意。 但是当涉及到实际使用时,故事会变得更加复杂。

最好的检测信号不仅仅是有效,它们还工作得悄无声息。 它们不会降低性能或用户信任度。 它们让您根据上下文做出决策,而不仅仅是在满足条件时触发不可逆转的操作。 最重要的是,它们具有适应性。

这个信号,虽然在演示中很滑稽且功能强大,但没有一个符合这些要求。 它很吵闹。 它是侵入性的。 它是脆弱的。

所以享受这个 bug 吧。 将它保留在您的工具包中。 嘲笑您在测试环境中崩溃的机器人。 但也许不要在生产环境中部署它。 尤其是在 Googlebot 可以看到它的地方。

除非您已经不在 Google 的搜索索引中。 那么,当然,放手去做吧。

Share Share Share Share Share Email Copy

接下来阅读

Fraudulent email domain tracker: April 2025 Research · Apr 29, 2025

Fraudulent email domain tracker: April 2025

这是 Castle 系列的第一个版本,重点介绍了与欺诈活动相关的电子邮件域。 我们的目标是

Understanding disposable emails Research · Apr 22, 2025

Understanding disposable emails

一次性电子邮件地址是临时收件箱,允许用户接收消息而无需将地址链接到长期身份。

How dare you trust the user agent for bot detection? Research · Apr 16, 2025

How dare you trust the user agent for bot detection?

在每个 HTTP 请求中,用户代理标头充当客户端(通常是浏览器)的自我声明的身份证 -

The Castle blog The Castle blog Research and insights on stopping modern bots and fraud Twitter RSS Bluesky Threads Linkedin Discord _ Download more icon variants from https://tabler-icons.io/i/brand-github _Github _ Download more icon variants from https://tabler-icons.io/i/brand-instagram _Instagram Pinterest Reddit _ Download more icon variants from https://tabler-icons.io/i/brand-telegram _Telegram _ Download more icon variants from https://tabler-icons.io/i/brand-tiktok _Tiktok _ Download more icon variants from https://tabler-icons.io/i/brand-whatsapp _Whatsapp _ Download more icon variants from https://tabler-icons.io/i/brand-youtube _Youtube The Castle blog ©2025 The Castle blog.