爬虫笔记之JS检测浏览器开发者工具是否打开

 

在某些状况下咱们须要检测当前用户是否打开了浏览器开发者工具,好比前端爬虫检测,若是检测到用户打开了控制台就认为是潜在的爬虫用户,再经过其它策略对其进行处理。本篇文章主要讲述几种前端JS检测开发者工具是否打开的方法。html

 image 

 

1、重写toString()

对于一些浏览器,好比Chrome、FireFox,若是控制台输出的是对象,则保留对象的引用,每次打开开发者工具的时候都会从新调用一下对象的toString()方法将返回结果打印到控制台(console tab)上。前端

因此只须要建立一个对象,重写它的toString()方法,而后在页面初始化的时候就将其打印在控制台上(这里假设控制台尚未打开),当用户打开控制台时会再去调用一下这个对象的toString()方法,用户打开控制台的行为就会被捕获到。git

下面是一个小小的例子,当Chrome用户的开发者工具状态从关闭向打开转移时,这个动做会被捕获到并交由回调函数处理:github

<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>

效果:chrome

 

当第一次在此页面打开控制台时会触发到检测,可是若是是在一个已经打开了控制台的窗口中粘贴网址访问则不会触发,同理在此页面上已经打开控制台时刷新也不会触发。浏览器

这种方式虽然比较取巧,可是并不具备通用性,而且只能捕获到开发者工具处于关闭状态向打开状态转移的过程,具备必定的局限性。app

 

2、debugger

相似于代码里的断点,浏览器在打开开发者工具时(对应于代码调试时的debug模式)检测到debugger标签(至关因而程序中的断点)的时候会暂停程序的执行:ide

image

此时须要点一下那个蓝色的“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>

效果:

 foo_001.gif

可是上面的代码有一个很严重的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

https://www.qimai.cn/

使用此种方案的话可能有个须要注意的点就是debugger是有可能不会暂停的,好比Chrome浏览器的source面板能够选择在debugger语句时不暂停:

image

若是这个按钮被点亮,再测试上面的网页就会发现很悲剧检测代码失效了,由于debugger标签根本就没有暂停。

 

其实debugger标签还有另外一种妙用,好比用来反调试,能够设定一个每秒就触发一个debugger,让调试者疲于应付debugger或者耗费他额外的成本去覆盖掉JS,上面给出的几个网站就是这么作的。

下面是一个使用debugger标签反js调试的简单例子:

<html>
<head>
    <title>Anti debug</title>
</head>
<body>
<script>

    !function () {
        setInterval(() => {
            debugger;
        }, 1000);
    }();

</script>
</body>
</html>

效果:

foo_001.gif

一个实际的例子,这个网站: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视频免费解析的,一旦检测到有人打开开发者工具在调试,就将解析好的视频移除掉,经过弹出一个提示框:

image

而当已经打开开发者工具再粘贴地址进行视频解析的话,将会触发无限debugger。

固然,应付上面脚本最简单的方法是把Chrome浏览器设定为Deactive breakpoint,上面的脚本就歇菜了,不过这样的话本身也没办法调试了,用来反调试确实可以恶心一下对面的家伙,比较好的方法是使用Fiddler修改网页返回内容过滤掉debugger标签能够完美破解此套路。

 

3、检测窗口大小

检测窗口大小比较简单,首先要明确两个概念,窗口的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浏览器来讲,开发者工具窗口有四个选项:单独窗口、靠左、靠下、靠右。

image

靠左、靠右、靠下都会占用当前窗口的一些空间,这种状况会被检测到,可是独立窗口并不会占用打开网页窗口的空间,因此这种状况是检测不到的,可去此页面进行验证:https://sindresorhus.com/devtools-detect/

 

4、总结

本文介绍了几种检测方式,其各有利弊,下面是对其缺点的一个简单的总结:

重写toString():只能捕获到开发者工具从关闭状态向打开状态转移的过程

debugger标签:当勾选了Chrome浏览器的Deactive breakpointimage ,debugger标签不会暂停,将捕获不到

检测窗口大小:当开发者工具是以独立窗口打开的时候不能检测到

 

 

相关资料:

1. Find out whether Chrome console is open

 

.

相关文章
相关标签/搜索