检测从JavaScript无头模式运行的Chrome

53 javascript google-chrome headless-browser

随着Chrome 59的发布,"无头"模式现在可用于Linux和macOS的稳定版本(很快就会推出带有Chrome 60的Windows).这使我们可以在没有任何可见UI的情况下运行功能齐全的Chrome版本,具有自动化测试的强大功能.这是一些例子.

chrome --headless --disable-gpu --dump-dom https://stackoverflow.com/
Run Code Online (Sandbox Code Playgroud)

在我的JavaScript测试运行器中,我喜欢尽可能多地记录有关正在使用的浏览器的信息,以帮助隔离问题.例如,我记录了许多属性navigator,包括当前的浏览器插件:

JSON.stringify(Array.from(navigator.plugins).map(p => p.name))
Run Code Online (Sandbox Code Playgroud)
["Chrome PDF Viewer","Widevine Content Decryption Module","Shockwave Flash","Native Client","Chrome PDF Viewer"]
Run Code Online (Sandbox Code Playgroud)

我的理解是Chrome 应该在无头模式下表现相同,但我有足够的经验对可能会显着改变渲染管道的新功能持怀疑态度.

现在,我将在两种模式下运行测试.我想让测试运行器记录是否正在使用无头模式.我可以在测试配置中传递这些信息,但我宁愿拥有一个纯JavaScript解决方案,我可以将其构建到测试运行器本身.但是,我无法找到任何显示无头模式是否处于活动状态的浏览器界面.

有没有办法检测Chrome是否在JavaScript无头模式下运行?

Jos*_*Lee 30

所述用户代理字符串包括HeadlessChrome代替Chrome.这可能是您打算寻找的信号,因此您可以使用:

/\bHeadlessChrome\//.test(navigator.userAgent)
Run Code Online (Sandbox Code Playgroud)

其他有趣的信号包括:

  • window.chrome当无头时,它看起来是不确定的.
  • [innerWidth, innerHeight][800, 600](硬编码headless_browser.cc),[outerWidth, outerHeight]而是[0, 0](通常不应该发生).

  • 用户代理字符串可以通过设置 chrome 选项来覆盖。 (2认同)

Jus*_*tas 16

您可以查看以下navigator.webdriver房产:

接口的webdriver只读属性navigator指示用户代理是否由自动化控制.

...

在以下情况下该navigator.webdriver属性为真:

Chrome使用--enable-automation--headless标志.
火狐marionette.enabled偏好或--marionette标志传递.

W3C WebDriver建议书描述如下:

navigator.webdriver 定义合作用户代理的标准方式,以通知文档它由WebDriver控制,例如,以便在自动化期间触发备用代码路径.


Hug*_* M. 8

刚刚阅读Antoine Vastel撰写的这篇文章,它提供了几种方法:

  • 测试用户代理/HeadlessChrome/.test(window.navigator.userAgent),但这很容易被欺骗
  • 测试插件 navigator.plugins.length == 0
  • 测试语言 navigator.languages == ""
  • 测试WebGL供应商和渲染器信息(有关详细信息,请参阅文章)
  • 测试由Modernizr检测到的支持功能:似乎不支持"hairlines"(hidpi/retina hairlines,其宽度小于1px的CSS边界,在hidpi屏幕上物理1px).测试是!Modernizr["hairline"].
  • 测试占位符的大小以查找丢失的图像.插入包含无效URL的图像,并测试image.width == 0 && image.height == 0in image.onerror(他们发现这个图像最强大).

不能代表Google的动机(Headless Chrome只是关于促进网络应用程序的测试吗?嗯...),但这可以看作是一个可能在某一天得到修复的错误列表,所以人们不得不怀疑这些错误有多长测试会工作:)


Fie*_*Cat 5

navigator.plugins应包含浏览器中存在的一系列插件(例如 Flash、ActiveX 或 Java 小程序)。对于headless浏览器来说它可以为空。

作为安全检查的一部分,它可以被使用alert,对于无头它将被忽略:

var start = Date.now();
alert('Press OK');
var elapse = Date.now() - start;
if (elapse < 15) {
    console.log("headless environment detected");
}
Run Code Online (Sandbox Code Playgroud)

Sergey Shekyan 和 Bei Zhu在 OWASP AppSecUSA 2014 演讲Headless Browser Hide & Seek视频幻灯片)中讨论了检测无头浏览器的几种技术。

  • 但我给你的结果是“假阳性”。我在 Android 手机上使用 Chrome,它返回 `navigator.plugins.length === 0`。所以,最好不要使用这个。 (3认同)

小智 5

到目前为止,我拥有的最好的解决方案是此hack。我不会在产品代码中使用它,但可能会在测试中使用它。

通常会为所有网站启用Chrome的弹出窗口阻止程序,但在无头模式下将其禁用。我们可以使用打开弹出窗口的功能作为处于无头模式的相当准确的代理。实现很简单:尝试open(...)打开一个窗口,然后检查是否得到了null(表明它已被阻止)而不是Window对象。如果我们确实打开了一个,请尽快将其关闭。

function canPopUp() {
  var w = open("");
  if (w !== null) {
    w.close();
    return true;
  } else {
    return false;
  }
}

var isHeadless = canPopUp;
Run Code Online (Sandbox Code Playgroud)

作为一个简单的示例,您可以尝试使用带有和不带有--headless标记的以下内容:

chrome --headless --disable-gpu --dump-dom 'data:text/html,<!doctype html><body><script>document.body.innerHTML = `headless: ${open("") !== null}`;</script>'