跳到主要内容

检测Headless Chrome

· 阅读需 4 分钟

什么是无头浏览器(Headless browser)?为什么要检测它?如何检测它?

什么是无头浏览器(Headless browser)?

无头浏览器是一种无需图形界面就可以使用的浏览器。可以通过程序控制无头浏览器来实现任务的自动化,比如进行测试、截屏、保存 PDF、爬虫等。

为什么要检测无头浏览器(Headless browser)?

无头浏览器会被用来批量自动化执行恶意程序,如数据爬虫、寻找网页漏洞、伪造访问数据等等。

PhantomJS 是曾经非常流行的一个无头浏览器。它是基于 Qt 框架开发的,因此与大多数浏览器不同,我们可以通过一些浏览器指纹识别技术来检测。

从 Chrome 59 开始,Google 发布了 Headless Chrome。与 PhantomJS 不同,它基于普通的 Chrome,大大增加了检测的难度。

无头浏览器(Headless browser)的检测

用户代理(User Agent)[有效]

if (/HeadlessChrome/.test(window.navigator.userAgent)) {
console.log('Chrome headless detected')
}

插件(Plugins)[有效]

navigator.plugins 返回浏览器插件数组。通常情况下,普通的 Chrome 浏览器中会有一些默认插件,如 Chrome PDF 阅读器或者 Chrome Google Native Client。但是在 headless 模式下,插件为空。

if (navigator.plugins.length == 0) {
console.log('It may be Chrome headless')
}

语言(Languages)[无效]

if (navigator.languages == '') {
console.log('Chrome headless detected')
}

WebGL [无效]

var canvas = document.createElement('canvas')
var gl = canvas.getContext('webgl')

var debugInfo = gl.getExtension('WEBGL_debug_renderer_info')
var vendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL)
var renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL)

if (vendor == 'Brian Paul' && renderer == 'Mesa OffScreen') {
console.log('Chrome headless detected')
}

Modernizr 检测 hairline

Modernizr可以测试浏览器中的 HTML 和 CSS 特性。Headless Chrome 不支持 hairline,这可以检测 hidpi/retina 对 hairline 的支持。

if (!Modernizr['hairline']) {
console.log('It may be Chrome headless')
}

图像 [无效]

如果是普通的 Chrome,图片的宽度和高度取决于浏览器的缩放,但与零不同。在无头的 Chrome 中,图片的宽度和高度都等于零。

var body = document.getElementsByTagName('body')[0]
var image = document.createElement('img')
image.src = 'http://iloveponeydotcom32188.jg'
image.setAttribute('id', 'fakeimage')
body.appendChild(image)
image.onerror = function () {
if (image.width == 0 && image.height == 0) {
console.log('Chrome headless detected')
}
}

Webdriver [有效]

if (navigator.webdriver) {
console.log('Chrome headless detected')
}

window.chrome [无效]

if (isChrome && !window.chrome) {
console.log('Chrome headless detected')
}

权限(Permissions)[有效]

Headless Chrome 不能正确的处理权限,会导致状态不一致,其中notifation.permissionnavigator.permissions.query的值是矛盾的。

navigator.permissions
.query({ name: 'notifications' })
.then(function (permissionStatus) {
if (
Notification.permission === 'denied' &&
permissionStatus.state === 'prompt'
) {
console.log('This is Chrome headless')
} else {
console.log('This is not Chrome headless')
}
})

参考资料