翻译前言:作为数据采集工程师经常和反爬虫技术做斗争,其中我使用的爬虫结构是:分布式+多机器+adsl | tor+phantomjs无界面浏览器+机器学习验证码破解/这样的结构已经基本属于爬虫界的大招。但是对方如果通过检测 phantomjs 的浏览器特性还是能区别出爬虫。于是翻译本文知己知彼,翻译功底不好切勿见怪,高手请移步文尾部可以看英语原文。
这些天,许多web安全事故涉及自动化。 Web-scraping、密码重用和点击欺诈攻击对手试图模拟真实用户,从而将请求看起来像是来自一个浏览器。作为网站的所有者,你想确保你的web是为人类服务。假设你有基本的检查cURL-like访客的能力,下一个合理的步骤是确保访客使用的是真正的ui驱动浏览器——而不是无头浏览器 PhantomJS 和 SlimerJS 。在本文中,我们将展示一些PhantomJS检测的技术。 我们决定专注于PhantomJS因为它是最受欢迎的无头浏览器环境,但许多的概念,我们将讨论适用于SlimerJS和其他工具。
目录:
- HTTP栈
- 客户端User-Agent
- 使用插件
- 定时
- 全局属性
- 缺乏JavaScript引擎的功能
- 堆栈跟踪
1: 检查HTTP栈
首先:它可以检测PhantomJS甚至在不用相应他(在获取请求头就可以检测他)吗?
如你所知,PhantomJS是建立在 Qt框架 。 Qt实现HTTP栈的方式使它突出于其他现代浏览器。
首先,让我们看看Chrome,发出以下head:
1 2 3 4 5 6 7 |
GET / HTTP/1.1 Host: localhost:1337 Connection: keep-alive Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 Accept-Encoding: gzip, deflate, sdch Accept-Language: en-US,en;q=0.8,ru;q=0.6 |
然而在PhantomJS,相同的HTTP请求是这样的:
1 2 3 4 5 6 7 |
GET / HTTP/1.1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.8 Safari/534.34 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Connection: Keep-Alive Accept-Encoding: gzip Accept-Language: en-US,* Host: localhost:1337 |
你会注意到PhantomJS头是不同于Chrome(事实证明,其他所有现代浏览器)有一些微妙的不同:
主机(host)
出现最后一行连接
头(Connection)是大小写混合- 唯一的
接受编码
值是gzip - User-Agent
在服务器上检查这些HTTP头的变化,它应该可以识别PhantomJS浏览器。
但是,相信这些值安全吗? 如果敌人使用一个代理修改标题前面的无头浏览器,他们可以修改这些标题显得象一个正常的现代浏览器。
看来解决这个问题纯粹只是在服务器上不是合适的。 让我们看看能做些什么在客户端,现在使用PhantomJS的JavaScript环境。
2: 客户端User-Agent
检查
我们可能无法通过HTTP信任User-Agent 的
值但是在客户端呢?
1 2 3 |
if (/PhantomJS/.test(window.navigator.userAgent)) { console.log("PhantomJS environment detected."); } |
不幸的是,它同样是可以被改变User-Agen和head 在PhantomJS 中检测userAgent值,这可能是不够的。
3: 使用插件
navigator.plugins
包含一个数组的插件在浏览器内。 典型的插件的价值观包括Flash,ActiveX,支持Java applet,“ 默认浏览器助手 ”,这是一个插件,表明这个浏览器是OS x的默认浏览器是否在我们的研究中,大多数新安装的常见的浏览器包括至少一个默认插件。
这是与PhantomJS,不实现任何插件,也不提供一种方法来添加一个(使用 PhantomJS API )。
以下检查可能会是有用的:
1 2 3 4 5 |
if (!(navigator.plugins instanceof PluginArray) || navigator.plugins.length == 0) { console.log("PhantomJS environment detected."); } else { console.log("PhantomJS environment not detected."); } |
另一方面,恶搞这个插件很简单数组通过修改PhantomJS JavaScript环境 在页面加载之前 。
也不难想象一个自定义构建的PhantomJS真实,实现插件。 这比听起来要容易得多,因为Qt PhantomJS构建提供了一个框架 本机API 实现插件。
4: 定时
另一个感兴趣的点是如何PhantomJS抑制JavaScript对话框:
1 2 3 4 5 6 7 8 |
var start = Date.now(); alert('Press OK'); var elapse = Date.now() - start; if (elapse < 15) { console.log("PhantomJS environment detected. #1"); } else { console.log("PhantomJS environment not detected."); } |
多次测量后,似乎如果警告对话框被限制了在15毫秒,浏览器可能不是被一个真实的人控制。 但使用这种方法意味着困扰真实用户与一个警告他们会手动关闭。
5: 全局属性
PhantomJS 1。 x暴露在全局对象两个属性:
1 2 3 4 5 |
if (window.callPhantom || window._phantom) { console.log("PhantomJS environment detected."); } else { console.log("PhantomJS environment not detected."); } |
然而,这些属性的一部分 实验功能 和在未来可能会改变。
6: 缺乏JavaScript引擎的功能
PhantomJS 1. x和2. x目前使用过时的WebKit引擎,这意味着有浏览器特性中存在的新浏览器PhantomJS并不存在。 这延伸到JavaScript引擎——即一些本机属性和方法是不同的或在PhantomJS缺席。其中一个方法是Function.prototype。 绑定,PhantomJS 下面的示例检查是否存在绑定,它没有被欺骗的执行环境。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
(function () { if (!Function.prototype.bind) { console.log("PhantomJS environment detected. #1"); return; } if (Function.prototype.bind.toString().replace(/bind/g, 'Error') != Error.toString()) { console.log("PhantomJS environment detected. #2"); return; } if (Function.prototype.toString.toString().replace(/toString/g, 'Error') != Error.toString()) { console.log("PhantomJS environment detected. #3"); return; } console.log("PhantomJS environment not detected."); })(); |
这段代码是有点太棘手的详细解释,但你可以找到更多 我们的演示 。
7: 堆栈跟踪
错误抛出的JavaScript代码由PhantomJS通过评估 评估 命令包含一个堆栈跟踪的唯一标识,我们可以确定无头浏览器。
例如,假设PhantomJS评估以下代码:
1 2 3 4 5 6 7 8 9 10 11 |
var err; try { null[0](); } catch (e) { err = e; } if (indexOfString(err.stack, 'phantomjs') > -1) { console.log("PhantomJS environment detected."); } else { console.log("PhantomJS environment is not detected."); } |
注意,这个示例使用一个定制的 indexOfString()
函数,留给读者作为练习,因为本机String.prototype.indexOf
可以欺骗PhantomJS总是返回一个负面的结果。
现在,你如何让PhantomJS脚本运行这段代码? 技术之一是覆盖一些经常使用DOM API函数可能被称为。 例如,下面的代码覆盖 document.querySelectorAll
检查浏览器的堆栈跟踪:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var html = document.querySelectorAll('html'); var oldQSA = document.querySelectorAll; Document.prototype.querySelectorAll = Element.prototype.querySelectorAll = function () { var err; try { null[0](); } catch (e) { err = e; } if (indexOfString(err.stack, 'phantomjs') > -1) { return html; } else { return oldQSA.apply(this, arguments); } }; |
总结
在本文中,我们研究了7个不同的技术来识别PhantomJS,都在服务器上,执行代码PhantomJS的客户端JavaScript环境。 结合检测结果与一个强大的反馈机制——例如,呈现动态页面惰性或无效当前会话cookie——你可以获得一个坚实的阻止PhantomJS访客的防火墙。 然而,总是记住这些技术并不可靠,和一个复杂的对手最终将获得通过。
为了了解更多,我们建议看的记录 从2014年美国AppSec我们的演示 ( 幻灯片 )。 我们也放在一起 GitHub库 实现示例和可能的危害,本文提供的技术。
感谢你的阅读,快乐狩猎。
贡献者:
- 链接分享: