从 JavaScript 以无头模式检测 Chrome 运行
Detect Chrome running in headless mode from JavaScript
随着Chrome59的发布,"headless"模式为now available in stable builds for Linux and macOS (and soon also Windows with Chrome 60). This allows us to run a full-featured version of Chrome without any visible UI, a great capability to have for automated testing. Here are examples.
chrome --headless --disable-gpu --dump-dom https://whosebug.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 应该 在无头模式下表现相同,但我有足够的经验对可能显着改变渲染管道的新功能持怀疑态度.
现在,我将 运行 在两种模式下进行测试。我想测试 运行ner 来记录是否正在使用无头模式。我可以在测试配置中传递此信息,但我宁愿有一个纯 JavaScript 解决方案,我可以将其构建到测试 运行ner 本身中。但是,我一直无法找到任何显示无头模式是否处于活动状态的浏览器界面。
有什么方法可以检测 Chrome 是否在 JavaScript 的无头模式下 运行?
navigator.plugins
应该包含一系列存在于浏览器中的插件(如 Flash、ActiveX 或 Java 小程序)。对于 headless
浏览器,它可以为空。
作为安全检查的一部分,它可以被使用 alert
,对于 headless 它将被忽略:
var start = Date.now();
alert('Press OK');
var elapse = Date.now() - start;
if (elapse < 15) {
console.log("headless environment detected");
}
Sergey Shekyan 和 Bei Zhang 在 OWASP AppSecUSA 2014 演讲 Headless Browser Hide & Seek (video, slides) 中讨论了几种检测无头浏览器的技术。
到目前为止,我拥有的最佳解决方案就是这个 hack。我不会在产品代码中使用它,但可能会在测试中使用它。
Chrome 的弹出窗口拦截器通常对所有网站都启用,但在 headless 模式下禁用。我们可以使用打开弹出窗口的能力作为处于无头模式的相当准确的代理。实现很简单:尝试open(...)
一个window,并检查我们是否得到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>'
用户代理 字符串includes HeadlessChrome
instead of Chrome
。这可能是您要查找的信号,因此您可以使用:
/\bHeadlessChrome\//.test(navigator.userAgent)
其他有趣的信号包括:
- 看起来
window.chrome
在无头时未定义。
[innerWidth, innerHeight]
是 [800, 600]
(在 headless_browser.cc
中硬编码),而 [outerWidth, outerHeight]
是 [0, 0]
(通常不应该发生)。
只需阅读 this article by Antoine Vastel 即可,其中提供了几种方法:
- 使用
/HeadlessChrome/.test(window.navigator.userAgent)
测试用户代理,但这很容易被欺骗
- 使用
navigator.plugins.length == 0
测试插件
- 使用
navigator.languages == ""
测试语言
- 测试 WebGL 供应商和渲染器信息(有关有趣的详细信息,请参阅文章)
- 正在测试 Modernizr 检测到的支持功能:似乎 "hairlines" 不受支持(hidpi/retina 细线,即宽度小于 1px 的 CSS 边框,物理上为 1px在 hidpi 屏幕上)。测试是
!Modernizr["hairline"]
.
- 正在测试缺失图像的占位符大小。插入带有无效 URL 的图像,并在
image.onerror
中测试 image.width == 0 && image.height == 0
(他们发现这个是最可靠的)。
不能代表Google的动机(是无头的Chrome只是为了促进网络应用程序的测试?嗯...),但这可以看作是有朝一日可能会修复的错误列表,因此人们不得不怀疑这些测试将工作多长时间:)
您可以查看 navigator.webdriver
属性 即:
The webdriver
read-only property of the navigator
interface indicates
whether the user agent is controlled by automation.
...
The navigator.webdriver
property is true when in:
Chrome The --enable-automation
or the --headless
flag is used.
Firefox The marionette.enabled
preference or --marionette
flag is passed.
W3C WebDriver推荐describes it如下:
navigator.webdriver
Defines a standard way for co-operating user agents to inform the document that it is controlled by WebDriver, for example so that alternate code paths can be triggered during automation.
随着Chrome59的发布,"headless"模式为now available in stable builds for Linux and macOS (and soon also Windows with Chrome 60). This allows us to run a full-featured version of Chrome without any visible UI, a great capability to have for automated testing. Here are examples.
chrome --headless --disable-gpu --dump-dom https://whosebug.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 应该 在无头模式下表现相同,但我有足够的经验对可能显着改变渲染管道的新功能持怀疑态度.
现在,我将 运行 在两种模式下进行测试。我想测试 运行ner 来记录是否正在使用无头模式。我可以在测试配置中传递此信息,但我宁愿有一个纯 JavaScript 解决方案,我可以将其构建到测试 运行ner 本身中。但是,我一直无法找到任何显示无头模式是否处于活动状态的浏览器界面。
有什么方法可以检测 Chrome 是否在 JavaScript 的无头模式下 运行?
navigator.plugins
应该包含一系列存在于浏览器中的插件(如 Flash、ActiveX 或 Java 小程序)。对于 headless
浏览器,它可以为空。
作为安全检查的一部分,它可以被使用 alert
,对于 headless 它将被忽略:
var start = Date.now();
alert('Press OK');
var elapse = Date.now() - start;
if (elapse < 15) {
console.log("headless environment detected");
}
Sergey Shekyan 和 Bei Zhang 在 OWASP AppSecUSA 2014 演讲 Headless Browser Hide & Seek (video, slides) 中讨论了几种检测无头浏览器的技术。
到目前为止,我拥有的最佳解决方案就是这个 hack。我不会在产品代码中使用它,但可能会在测试中使用它。
Chrome 的弹出窗口拦截器通常对所有网站都启用,但在 headless 模式下禁用。我们可以使用打开弹出窗口的能力作为处于无头模式的相当准确的代理。实现很简单:尝试open(...)
一个window,并检查我们是否得到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>'
用户代理 字符串includes HeadlessChrome
instead of Chrome
。这可能是您要查找的信号,因此您可以使用:
/\bHeadlessChrome\//.test(navigator.userAgent)
其他有趣的信号包括:
- 看起来
window.chrome
在无头时未定义。 [innerWidth, innerHeight]
是[800, 600]
(在headless_browser.cc
中硬编码),而[outerWidth, outerHeight]
是[0, 0]
(通常不应该发生)。
只需阅读 this article by Antoine Vastel 即可,其中提供了几种方法:
- 使用
/HeadlessChrome/.test(window.navigator.userAgent)
测试用户代理,但这很容易被欺骗 - 使用
navigator.plugins.length == 0
测试插件
- 使用
navigator.languages == ""
测试语言
- 测试 WebGL 供应商和渲染器信息(有关有趣的详细信息,请参阅文章)
- 正在测试 Modernizr 检测到的支持功能:似乎 "hairlines" 不受支持(hidpi/retina 细线,即宽度小于 1px 的 CSS 边框,物理上为 1px在 hidpi 屏幕上)。测试是
!Modernizr["hairline"]
. - 正在测试缺失图像的占位符大小。插入带有无效 URL 的图像,并在
image.onerror
中测试image.width == 0 && image.height == 0
(他们发现这个是最可靠的)。
不能代表Google的动机(是无头的Chrome只是为了促进网络应用程序的测试?嗯...),但这可以看作是有朝一日可能会修复的错误列表,因此人们不得不怀疑这些测试将工作多长时间:)
您可以查看 navigator.webdriver
属性 即:
The
webdriver
read-only property of thenavigator
interface indicates whether the user agent is controlled by automation....
The
navigator.webdriver
property is true when in:Chrome The
--enable-automation
or the--headless
flag is used.
Firefox Themarionette.enabled
preference or--marionette
flag is passed.
W3C WebDriver推荐describes it如下:
navigator.webdriver
Defines a standard way for co-operating user agents to inform the document that it is controlled by WebDriver, for example so that alternate code paths can be triggered during automation.