任何网页开发者出于对自己网页的保护,都不想自己的前端代码被他人窃取或者抄袭。虽然我们平时都在忙着往代码里面写BUG,但是依然阻止他人对你代码的觊觎。
由此,前端反调试需求应运而生。
无限debugger反调试
(function() {
setInterval(() => {
debugger;
}, 50);
}())
1、一定要使用匿名函数。否则别人直接在控制台重定义函数就完了,比如:
function startDebug() {};
2、一定要写在一行。否则别人可以通过设置条件断点false
,来使debugger失效。
代码虽然简单,但是却很有效。
它会不停地打断你,页面跳到source页面,阻止你看他代码不断的产生不可回收的对象,占据你的内存,造成内存泄漏,没过多久浏览器就会卡顿。
缺点:可以通过禁用浏览器断点功能(Deactivate breakpoints)来禁止debugger,但是也存在很大的缺陷,因为对于其他代码,我们还是需要断点调试功能的。所以这个方法仅限于静态分析。
禁用F12等按键
没啥好说的,直接上代码。
document.oncontextmenu = function () {//右键禁用
return false;
};
document.oncopy = function () {//复制禁用
return false;
};
document.addEventListener('keydown', function (e) {//按键禁用
try {
e = e || event || window.event;
var keycode = e.keyCode || e.which || e.charCode;
var ctrlKey = e.ctrlKey || e.metaKey;
if (keycode == 123) {//F12
e.preventDefault();
window.event.returnValue = false;
} else if (keycode == 8) { // BackSpace
var act = document.activeElement.tagName.toLowerCase();
if (act.indexOf("body") != -1 || act.indexOf("html") != -1) {
window.history.back();
return false;
}
return true;
} else if (ctrlKey && keycode == 83) {//ctrl+S
e.preventDefault();
window.event.returnValue = false;
} else if (ctrlKey && keycode == 87) {//ctrl+W
e.preventDefault();
window.event.returnValue = false;
} else if (ctrlKey && keycode == 107) {//ctrl++
e.preventDefault();
window.event.returnValue = false;
} else if (ctrlKey && keycode == 109) {//ctrl+-
e.preventDefault();
window.event.returnValue = false;
} else if (keycode == 122) {//F11
$('#switchFullScreenCloseBtn').trigger("click");
}
} catch (e) {
console.error(e);
}
});
“return false” VS “ event.preventDefault()” VS “event.stopPropagation()” 三者的区别?
event.preventDefault();
阻止浏览器默认行为event.stopPropagation()
:阻止事件冒泡return false
:
- 在jQuery中,等于前两者之和。
- 在非jQuery中,等于event.preventDefault(); ,并不能阻止事件冒泡。
参考链接:https://stackoverflow.com/questions/1357118/event-preventdefault-vs-return-false
禁用F12等按键并debugger
通过第二步,禁止F12等按键后,虽然无法使用快捷键进入调试工具,但是如果通过浏览器右上角的【设置】,进入【更多工具】,再打开【开发人员工具】,调试界面还是可以打开的。
那么我们就要在他进入后,也要打开debugger
,所以我们在第二步的基础上进行优化。
直接上代码了:
/**
* 前端反调试 (Daotin added in 20210316)
* closeDebugger: 关闭debugger
* openDebugger: 打开debugger
*/
export default {
closeDebugger: function () {
/*
* 反调试代码,如果还是打开了控制台,无限debugger,并且网页变得极其卡顿,原因未知(类似问卷星答题界面)
*/
window._debuggerTimer = setInterval(function() { check() }, 5000); // 每5秒执行一次
let check = function() {
function doCheck(a) {
if (("" + a/a)["length"] !== 1 || a % 20 === 0) {
( function() {} ["constructor"]("debugger")() ) // 类似 new Function('debugger')()
} else {
( function() {} ["constructor"]("debugger")() )
}
console.log('----->无限debugger');
doCheck(++a);
}
try { doCheck(0) } catch (err) {}
};
check();
/**
* 按键禁用
*/
$(document).on('keydown',function (event) {
let e = e || event || window.event;
let keyCode = e.keyCode || e.which || e.charCode;
let ctrlkey = e.ctrlKey || e.metaKey;
if (keyCode == 122 || keyCode == 123) { // Prevent F11/F12
return false;
} else if (ctrlkey && keyCode == 83) { // Prevent Ctrl+S
let saveBtn = $('.answer-start .savebtn')[0];
console.log('saveBtn----->', saveBtn);
if(saveBtn) {
// 使用 $(savaBtn).trigger('click'); 无效。
saveBtn.click(); // 触发自己的保存
}
return false;
} else if (ctrlkey && keyCode == 87) { // Prevent Ctrl+W
return false;
} else if(keyCode == 8) { // Bankspace 键可以返回
// var act = document.activeElement.tagName.toLowerCase();
// if (act.indexOf("body") != -1 || act.indexOf("html") != -1) {
// window.history.back();
// return false;
// }
// return true;
}
});
$(document).on("contextmenu", function (e) { //右键禁用
return false;
});
$(document).on("copy", function (e) { //复制禁用
return false;
});
console.log('----->', '前端防调试open');
},
openDebugger: function () {
clearInterval(window._debuggerTimer);
window._debuggerTimer = null;
$(document).off('keydown');
$(document).off("contextmenu");
$(document).off("copy");
console.log('----->', '前端防调试close');
}
}
加了一段反调试代码,如果还是打开了控制台,无限debugger,并且网页变得极其卡顿。
另一种写法
增加utils/debugger.js
文件,并在main.ts中引入该文件。当生产环境需要调试时,在控制台输入window.__debugger__ = true
即可。
const forbid = () => {
function block() {
const interval = setInterval(() => {
if (window.__debugger__ == true) return clearInterval(interval);
(function () {
return false;
}
["constructor"](["d", "e", "b", "u", "g", "g", "e", "r"].join(""))
["call"]());
}, 50);
}
try {
block();
} catch (err) {}
};
import.meta.env.MODE === "production" && forbid();
参考链接:
- https://app.yinxiang.com/fx/f30b911e-8e33-4823-bcb6-4fcf89274b17
- https://segmentfault.com/a/1190000011461827
- https://blog.csdn.net/chouyidang1008/article/details/100946392
扩展
这样我们的前端反调试就完成了,能防住一些基本的小白用户,但是无法防住真正的大神。
那么,“反反调试”也出现了,真是道高一尺,魔高一丈。
可以参考这篇文章:前端Chrome反《反调试》
还有这篇文章,可以拦截debugger:【第2937期】JavaScript奇技淫巧:debugger拦截
总结:
- Chrome浏览器使用,条件断点设为false即可
- Firefox,使用
Function.prototype.constructor=function(){}
直接破解…
(完)