如今,许多Web安全事件都涉及自动化操作。攻击者会试图模仿真实用户,让Web抓取、密码重用和点击欺诈攻击看起来像是来自浏览器。作为网站所有者,您需要确保网站服务的是真实用户;作为Web服务提供商,您希望对内容的程序化访问通过API进行,而不是通过负载更重、稳定性更低的Web界面抓取内容。
假设您已对使用cURL等工具的访问者实施基本检查,下一步可以确认访问者使用的是真正由用户界面驱动的浏览器,而不是PhantomJS和SlimerJS等无头浏览器。
本文将介绍一些识别 PhantomJS 访问的技术。我们之所以重点介绍 PhantomJS,是因为它是一种广受使用的无头浏览器环境,但本文涉及的许多概念也适用于 SlimerJS 和其他工具。
注意:除非另有明确说明,本文介绍的技术同时适用于PhantomJS 1.x和2.x。首先来看:是否可以在不向PhantomJS发送任何响应的情况下检测到它?
HTTP 协议栈
您可能已经知道,PhantomJS基于Qt框架构建。Qt实现HTTP协议栈的方式使其有别于其他现代浏览器。
首先来看一下Chrome,它会发送以下标头:

但在PhantomJS中,同一个HTTP请求如下所示:

您会注意到,PhantomJS标头与Chrome(事实上也与所有其他现代浏览器)存在一些细微差异:
- Host 标头位于最后
- Connection 标头值采用大小写混合形式
- Accept-Encoding 的唯一值为 gzip
- User-Agent包含“PhantomJS”
通过在服务器端检查这些HTTP标头异常,应该可以识别出PhantomJS浏览器。
但是,这些值真的可信吗?如果攻击者在无头浏览器前使用代理重写标头,就可以修改这些标头,使其看起来像来自普通的现代浏览器。
看来,仅在服务器端解决这个问题并非万全之策。因此,我们来看看利用PhantomJS的JavaScript环境可以在客户端采取哪些措施。
客户端User-Agent检查
我们可能无法信任通过HTTP传递的User-Agent值,但客户端上的情况又如何呢?

遗憾的是,在PhantomJS中更改user-agent标头和navigator.userAgent值同样轻而易举,因此仅凭这一点可能还不够。
插件
navigator.plugins 包含一个由浏览器中现有插件组成的数组。常见的插件值包括Flash、ActiveX、Java小程序支持,以及“默认浏览器助手”。该插件用于指示此浏览器是否为OS X中的默认浏览器。我们的研究发现,大多数常用浏览器在全新安装后都至少包含一个默认插件,移动设备上的浏览器也不例外。
这与PhantomJS不同,后者未实现任何插件,也未提供添加插件的方法(使用PhantomJS API)。
此时,以下检查可能会有所帮助:

另一方面,通过修改PhantomJS JavaScript环境,可以轻易伪造此插件数组,修改需在页面加载之前完成。
也不难想象,可以定制构建一个配备真正可用插件的PhantomJS。实现起来比想象中更容易,因为PhantomJS所基于的Qt框架提供了用于实现插件的原生API。
时序
另一个值得关注的方面是PhantomJS如何阻止JavaScript对话框显示:

经过多次测量后发现,如果警告对话框在15毫秒内被关闭,则浏览器很可能并非由真人操控。但这种方法会打扰真实用户,因为他们必须手动关闭警告对话框。
全局属性
PhantomJS 1.x在全局对象上公开了两个属性:

不过,这些属性属于一项实验性功能,未来可能会发生变化。
缺少JavaScript引擎功能
PhantomJS 1.x和2.x目前使用的是过时的WebKit引擎,因此,新版浏览器中的某些浏览器功能在PhantomJS中并不存在。JavaScript引擎也存在这一问题,PhantomJS中的某些原生属性和方法有所不同或缺失。
其中一种方法是Function.prototype.bind,PhantomJS 1.x及更早版本不支持该方法。以下示例会检查bind是否存在,以及它在执行环境中是否遭到伪造。

这段代码有些复杂,不便在此详细说明,但可以通过我们的演示文稿了解更多信息。
堆栈跟踪
由PhantomJS通过evaluate命令求值的JavaScript代码所抛出的错误包含可唯一识别的堆栈跟踪,据此可以识别无头浏览器。
例如,假设PhantomJS对以下代码调用evaluate:

请注意,此示例使用了自定义的 indexOfString() 函数,其实现留给读者自行完成,因为 PhantomJS 可以伪造原生 String.prototype.indexOf 的行为,使其始终返回负值。
那么,如何让PhantomJS脚本执行这段代码?一种方法是重写一些可能会被调用的常用DOM API函数。例如,以下代码会重写document.querySelectorAll,以检查浏览器的堆栈跟踪:

摘要
本文介绍了7种识别PhantomJS的不同技术,既包括在服务器端进行识别,也包括在PhantomJS客户端JavaScript环境中执行代码进行识别。将检测结果与强有力的反馈机制相结合,例如使动态页面失效或使当前会话Cookie失效,可以有效阻碍PhantomJS访问者。但请始终注意,这些技术并非万无一失,经验丰富的攻击者最终仍可能绕过这些措施。
如需了解更多信息,建议观看我们在AppSec USA 2014上的演讲录像(幻灯片)。我们还创建了一个GitHub存储库,其中包含本文所述技术的实现示例以及可能的规避方法。
感谢阅读,祝您顺利发现漏洞。
贡献者
Sergey Shekyan – @sshekyan
Ben Vinegar – @bentlegen
Bei Zhang – @ikarienator
关于作者
相关博客文章
Thingbot正转变为攻击平台
Lori MacVittie重点介绍了F5 Labs对2018年1月至6月全球物联网设备攻击的研究,并指出了其中令人警醒的影响。物联网设备不仅持续遭到利用,还被转变为攻击平台,这意味着攻击者可以更充分地利用这个必然增长的市场。
借助F5全新Quick Start,在AWS上快速开展业务
在AWS re:Invent开幕前,我们很高兴与大家分享F5与AWS之间全新的Quick Start集成。F5的Tom Atkins将介绍该服务如何启动、配置和运行在AWS上部署工作负载所需的计算、网络、存储及其他AWS服务。