爬虫笔记之JS检测浏览器开发者工具是否打开
在某些情况下我们需要检测当前用户是否打开了浏览器开发者工具,比如前端爬虫检测,如果检测到用户打开了控制台就认为是潜在的爬虫用户,再通过其它策略对其进行处理。本篇文章主要讲述几种前端JS检测开发者工具是否打开的方法。
一、重写toString()
对于一些浏览器,比如Chrome、FireFox,如果控制台输出的是对象,则保留对象的引用,每次打开开发者工具的时候都会重新调用一下对象的toString()方法将返回结果打印到控制台(console tab)上。
所以只需要创建一个对象,重写它的toString()方法,然后在页面初始化的时候就将其打印在控制台上(这里假设控制台还没有打开),当用户打开控制台时会再去调用一下这个对象的toString()方法,用户打开控制台的行为就会被捕获到。
下面是一个小小的例子,当Chrome用户的开发者工具状态从关闭向打开转移时,这个动作会被捕获到并交由回调函数处理:
<html>
<head>
<title>console detect test</title>
</head>
<body>
<script> /**
* 控制台打开的时候回调方法
*/
function consoleOpenCallback(){
alert("CONSOLE OPEN");
return "";
} /**
* 立即运行函数,用来检测控制台是否打开
*/
!function () {
// 创建一个对象
let foo = /./;
// 将其打印到控制台上,实际上是一个指针
console.log(foo);
// 要在第一次打印完之后再重写toString方法
foo.toString = consoleOpenCallback;
}() </script>
</body>
</html>
效果:
当第一次在此页面打开控制台时会触发到检测,但是如果是在一个已经打开了控制台的窗口中粘贴网址访问则不会触发,同理在此页面上已经打开控制台时刷新也不会触发。
这种方式虽然比较取巧,但是并不具有通用性,并且只能捕获到开发者工具处于关闭状态向打开状态转移的过程,具有一定的局限性。
二、debugger
类似于代码里的断点,浏览器在打开开发者工具时(对应于代码调试时的debug模式)检测到debugger标签(相当于是程序中的断点)的时候会暂停程序的执行:
此时需要点一下那个蓝色的“Resume script execution”程序才会继续执行,这中间会有一定的时间差,通过判断这个时间差大于一定的值就认为是打开了开发者工具。这个方法并不会误伤,当没有打开开发者工具时遇到debugger标签不会暂停,所以这种方法还是蛮好的,而且通用性比较广。
下面是一个使用debugger标签检测开发者工具是否打开的例子:
<html>
<head></head>
<body>
<script> function consoleOpenCallback() {
alert("CONSOLE OPEN");
} !function () {
const handler = setInterval(() => {
const before = new Date();
debugger;
const after = new Date();
const cost = after.getTime() - before.getTime();
if (cost > 100) {
consoleOpenCallback();
clearInterval(handler)
}
}, 1000)
}(); </script>
</body>
</html>
效果:
但是上面的代码有一个很严重的bug,就是在执行到debugger那一行的时候如果用户发现了猫腻没有点按resume script execution按钮,而是直接退出页面的话,那么将不能检测到本次的打开开发者工具行为,实际结果与预期不符,我认为这是严重bug,就像电影里演的不小心踩到地雷及时察觉不抬脚就还有活命机会,到了debugger标签我察觉到这是检测控制台是否打开的代码我退出然后使用其它手段绕过它,那我可能做了一个假功能。
有一个需要注意的地方就是使用此方法的时候当卡在debugger标签的时候,用户是能够看到debugger标签附近的代码的,如果是有经验的用户一眼就能看出里面的道道,所以要想办法隐藏一下真实目的,比如将debugger标签隐藏,并且对代码进行混淆尽量增加阅读难度,关于如何隐藏debugger标签前后的逻辑,可以参考这几个网站:(当前2018-7-4 23:12:17有效)
http://app2.sfda.gov.cn/datasearchp/gzcxSearch.do?formRender=cx&optionType=V1
使用此种方案的话可能有个需要注意的点就是debugger是有可能不会暂停的,比如Chrome浏览器的source面板可以选择在debugger语句时不暂停:
如果这个按钮被点亮,再测试上面的网页就会发现很悲剧检测代码失效了,因为debugger标签根本就没有暂停。
其实debugger标签还有另一种妙用,比如用来反调试,可以设定一个每秒就触发一个debugger,让调试者疲于应付debugger或者耗费他额外的成本去覆盖掉JS,上面给出的几个网站就是这么做的。
下面是一个使用debugger标签反js调试的简单例子:
<html>
<head>
<title>Anti debug</title>
</head>
<body>
<script> !function () {
setInterval(() => {
debugger;
}, 1000);
}(); </script>
</body>
</html>
效果:
一个实际的例子,这个网站:http://jxw.uou0.com/的js检测脚本,而针对不同的情况它又会有不同的反调试策略。
注意要想复现需要粘贴视频地址解析之后才会加载检测脚本,比如可以尝试解析这个视频:http://film.qq.com/film/p/topic/thwjlxby/index.html。
当未打开开发者工具进行解析,然后打开开发者工具,则会使用这种检测方式:
!function() {
var timelimit = 50;
var open = false;
setInterval(function() {
var starttime = new Date();
debugger ;if (new Date() - starttime > timelimit) {
open = true;
window.stop();
$("#loading").hide();
$("#a1").remove();
$("#error").show();
$("#error").html("\u7cfb\u7edf\u68c0\u6d4b\u975e\u6cd5\u8c03\u8bd5\u002c\u8bf7\u5237\u65b0\u91cd\u8bd5\u0021")
} else {
open = false
}
}, 500)
}();
因为这个网站是做vip视频免费解析的,一旦检测到有人打开开发者工具在调试,就将解析好的视频移除掉,通过弹出一个提示框:
而当已经打开开发者工具再粘贴地址进行视频解析的话,将会触发无限debugger。
当然,应付上面脚本最简单的方法是把Chrome浏览器设定为Deactive breakpoint,上面的脚本就歇菜了,不过这样的话自己也没办法调试了,用来反调试确实能够恶心一下对面的家伙,比较好的方法是使用Fiddler修改网页返回内容过滤掉debugger标签可以完美破解此套路。
三、检测窗口大小
检测窗口大小比较简单,首先要明确两个概念,窗口的outer大小和inner大小:
window.innerWidth / window.innerHeight :可视区域的宽高,window.innerWidth包含了纵向滚动条的宽度,window.innerHeight包含了水平(横向)滚动条的宽度。
window.outerWidth / window.outerHeight:会在innerWidth和innerHeight的基础上加上工具条的宽度。
关于检测窗口大小,不再自己写例子,有人专门针对此写了个库:https://github.com/sindresorhus/devtools-detect,毕竟几百个star,比我这个渣渣写的好多了,代码比较简单,使用部分其github都有说明,这里只对其核心代码做个分析,此处贴出鄙人对此库核心代码的分析:
/* eslint-disable spaced-comment */
/*!
devtools-detect
Detect if DevTools is open
https://github.com/sindresorhus/devtools-detect
by Sindre Sorhus
MIT License
comment by CC11001100
*/
(function () {
'use strict';
var devtools = {
open: false,
orientation: null
};
// inner大小和outer大小超过threshold被认为是打开了开发者工具
var threshold = 160;
// 当检测到开发者工具后发出一个事件,外部监听此事件即可,设计得真好,很好的实现了解耦
var emitEvent = function (state, orientation) {
window.dispatchEvent(new CustomEvent('devtoolschange', {
detail: {
open: state,
orientation: orientation
}
}));
}; // 每500毫秒检测一次开发者工具的状态,当状态改变时触发事件
setInterval(function () {
var widthThreshold = window.outerWidth - window.innerWidth > threshold;
var heightThreshold = window.outerHeight - window.innerHeight > threshold;
var orientation = widthThreshold ? 'vertical' : 'horizontal'; // 第一个条件判断没看明白,heightThreshold和widthThreshold不太可能同时为true,不论是其中任意一个false还是两个都false取反之后都会为true,此表达式恒为true
if (!(heightThreshold && widthThreshold) &&
// 针对Firebug插件做检查
((window.Firebug && window.Firebug.chrome && window.Firebug.chrome.isInitialized) || widthThreshold || heightThreshold)) {
// 开发者工具打开,如果之前开发者工具没有打开,或者已经打开但是靠边的方向变了才会发送事件
if (!devtools.open || devtools.orientation !== orientation) {
emitEvent(true, orientation);
} devtools.open = true;
devtools.orientation = orientation;
} else {
// 开发者工具没有打开,如果之前处于打开状态则触发事件报告状态
if (devtools.open) {
emitEvent(false, null);
} // 将标志位恢复到未打开
devtools.open = false;
devtools.orientation = null;
}
}, 500); if (typeof module !== 'undefined' && module.exports) {
module.exports = devtools;
} else {
window.devtools = devtools;
} })();
缺点:
1. 使用window属性检查大小可能会有浏览器兼容性问题,因为不是专业前端只测试了Chrome和ff是没有问题的。
2. 此方案还是有漏洞的,就拿Chrome浏览器来说,开发者工具窗口有四个选项:单独窗口、靠左、靠下、靠右。
靠左、靠右、靠下都会占用当前窗口的一些空间,这种情况会被检测到,但是独立窗口并不会占用打开网页窗口的空间,所以这种情况是检测不到的,可去此页面进行验证:https://sindresorhus.com/devtools-detect/。
四、总结
本文介绍了几种检测方式,其各有利弊,下面是对其缺点的一个简单的总结:
重写toString():只能捕获到开发者工具从关闭状态向打开状态转移的过程
debugger标签:当勾选了Chrome浏览器的Deactive breakpoint ,debugger标签不会暂停,将捕获不到
检测窗口大小:当开发者工具是以独立窗口打开的时候不能检测到
相关资料:
1. Find out whether Chrome console is open
.
爬虫笔记之JS检测浏览器开发者工具是否打开的更多相关文章
- js 检测浏览器开发者控制台是否被打开
var element = new Image(); Object.defineProperty(element, "id", { get: function () { debug ...
- 爬虫 Http请求,urllib2获取数据,第三方库requests获取数据,BeautifulSoup处理数据,使用Chrome浏览器开发者工具显示检查网页源代码,json模块的dumps,loads,dump,load方法介绍
爬虫 Http请求,urllib2获取数据,第三方库requests获取数据,BeautifulSoup处理数据,使用Chrome浏览器开发者工具显示检查网页源代码,json模块的dumps,load ...
- [转]谷歌Chrome浏览器开发者工具教程—JS调试篇
来源:http://blog.csdn.net/cyyax/article/details/51242720 上一篇我们学习了谷歌Chrome浏览器开发者工具的基础功能,下面介绍的是Chrome开发工 ...
- 谷歌Chrome浏览器开发者工具的基础功能
上一篇我们学习了谷歌Chrome浏览器开发者工具的基础功能,下面介绍的是Chrome开发工具中最有用的面板Sources.Sources面板几乎是最常用到的Chrome功能面板,也是解决一般问题的主要 ...
- Google Chrome谷歌/火狐/Safari浏览器开发者工具基本使用教程
前言 在阅读下面内容之前,那么些简单的了解浏览器开发者工具到底是什么东西,到底有什么用途. 浏览器开发者工具到底是什么? 其实简单的说,浏览器开发者工具就是给专业的web应用和网站开发人员使用的工具, ...
- 浏览器开发者工具console
浏览器开发者工具基本使用教程 谷歌Chrome浏览器开发者工具教程-基础功能篇 - 算命de博客 - CSDN博客 JavaScript Console 对象 | 菜鸟教程
- jquery和js检测浏览器窗口尺寸和分辨率
jquery和js检测浏览器窗口尺寸和分辨率,转载自网络,记录备忘 <script type="text/javascript">$(document).ready(f ...
- 使用Google浏览器开发者工具学习HTTP请求记录
GET请求 1.Google浏览器开发者工具截图图示 2.General Request URL :为请求链接 Status Code :为HTTP响应状态码 3.ResponseHeaders :响 ...
- chrome浏览器开发者工具F12中某网站的sources下的源码如何批量保存?
目录 chrome浏览器 开发者工具F12中某网站的sources下的源码如何批量保存 1. 常用保存Sources源码的两种方法 1.1单个文件 1.2 单个页面 2. 问题 3.解决方案 chro ...
随机推荐
- [转帖]高通推出八核笔电处理器骁龙8cx 能超英特尔吗?
高通推出八核笔电处理器骁龙8cx 能超英特尔吗? https://baijiahao.baidu.com/s?id=1619154699684981202&wfr=spider&for ...
- 1使用 vue-cli 搭建项目(cp)
http://www.cnblogs.com/wisewrong/p/6255817.html(copy:web) https://zhuanlan.zhihu.com/p/26183652(也很好) ...
- SPOJ IM_Intergalactic Map
判断能否从一个点同时找出两条不相交的路径到另外两个点. 保证路径不相交,那么需要拆点.然后?好像就没什么了,直接最大流即可. 不过,,,不需要求出所有的最大流,只要跑两次EK看看能否增广两次就行了. ...
- bzoj2095-Bridge
题意 一个 \(n\) 个点 \(m\) 条边的图,每条边双向都有权值(可能不一样).求从 1 开始,经过所有点,经过所有边一次且仅一次(即一定要经过这条边的某个方向)回到 1 的路径上权值最大的最小 ...
- P4314 CPU监控
题面 这是一道堪称"线段树3"的线段树好题,对于\(lazy\)标记的操作可以说是非常巧妙 我们用结构体来记录\(lazy\)标记,结构体中定义\(a,b\)两个元素,\(a\)表 ...
- c++11 可变参数模板类
c++11 可变参数模板类 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #inc ...
- MT【137】多少个?
数列\(\{a_n\}\)共11项,\(a_1=0,a_{11}=4\),且\(|a_{k+1}-a_{k}|=2,k=1,2,\cdots,10\) 求满足条件的不同的数列的个数______ 解答: ...
- 【转】每个 Linux 新手都应该知道的 10 个命令
你可能认为你是 Linux 新手,但实际上并不是.全球互联网用户有 3.74 亿,他们都以某种方式使用 Linux,因为 Linux 服务器占据了互联网的 90%.大多数现代路由器运行 Linux 或 ...
- USACO Section 2.1 Ordered Fractions 解题报告
题目 题目描述 给定一个数N(1<=N<=160),需要产生所有的分数,这些分数的值必须要在0~1之间.而且每个分数的分母不能超过N.如下例所示: N = 5 产生所有的分数:0/1 1/ ...
- 【ARC083E】Bichrome Tree
Description 给一棵\(n\)个节点的树,和一个长度同样为\(n\)的非负整数序列\(x_i\). 请尝试对每个节点染黑或白两种颜色,并确定一个非负整数权值. 问是否存在一种方案 ...