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

时间:2017-06-06 18:38:22

标签: javascript google-chrome headless-browser

随着Chrome 59的发布,“无头”模式为now available,适用于Linux和macOS的稳定版本(很快就会推出带有Chrome 60的Windows)。这使我们可以在没有任何可见UI的情况下运行功能齐全的Chrome版本,具有自动化测试的强大功能。 Here are examples.

chrome --headless --disable-gpu --dump-dom https://stackoverflow.com/

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

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

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

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

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

5 个答案:

答案 0 :(得分:29)

用户代理字符串includes HeadlessChrome instead of Chrome。这可能是您打算寻找的信号,因此您可以使用:

/\bHeadlessChrome\//.test(navigator.userAgent)

其他有趣的信号包括:

  • 当无头时,看起来window.chrome未定义。
  • [innerWidth, innerHeight][800, 600](在headless_browser.cc中硬编码),而[outerWidth, outerHeight][0, 0](通常不应发生)。

答案 1 :(得分:15)

您可以检查navigator.webdriver属性,即:

  

webdriver界面的navigator只读属性表示   用户代理是否由自动化控制。

     

...

     

在以下情况下,navigator.webdriver属性为true

:      

Chrome 使用--enable-automation--headless标志。
   Firefox 已通过marionette.enabled首选项或--marionette标志。

W3C WebDriver建议describes it如下:

  

navigator.webdriver 定义了一种标准方法,用于使用户代理协作来通知文档它由WebDriver控制,例如,以便可以在自动化过程中触发备用代码路径。

答案 2 :(得分:7)

请阅读this article by 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"]
  • 测试占位符的大小以查找丢失的图像。插入包含无效网址的图片,并在image.width == 0 && image.height == 0中测试image.onerror(他们认为这是最强大的)。

不能代表谷歌的动机(是无头Chrome只是为了促进网络应用程序的测试?嗯...... ),但这可以看作是一个列表有一天可能会修复的错误,所以我们不得不怀疑这些测试能运行多长时间:)

答案 3 :(得分:4)

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");
}

OWASP AppSecUSA 2014 talk Headless Browser Hide&amp; amp;由Sergey Shekyan和Bei Zhang寻求videoslides)。

答案 4 :(得分:4)

我到目前为止最好的解决方案就是这个黑客攻击。我不会在prod代码中使用它,但可能会在测试中使用它。

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

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

var isHeadless = canPopUp;

有关快速示例,您可以使用和不使用--headless标志尝试以下内容:

  

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