前端代码混淆后怎么防调试?控制台一打就崩了
我最近在搞一个付费的H5小游戏,为了防止别人直接扒代码,用了webpack加了Terser做混淆,还加了点反调试的代码。但上线后发现,只要用户打开DevTools,页面就直接白屏或者卡死,体验太差了。我试过网上那种覆盖console.log的方法,但好像没用。
比如下面这段简单的防调试代码,本地测试没问题,但放线上就不行:
<script>
(function() {
let devtools = false;
setInterval(() => {
if (devtools) return;
console.log('check');
devtools = /./.test(/./);
}, 1000);
})();
</script>
有没有更稳妥的防调试方案?既不让代码被轻易读,又不至于把正常用户搞崩溃?
一种稍微复杂但更可靠的方案是结合多种检测方法,并且确保这些检测不会过于频繁地影响性能。可以尝试以下步骤:
1. 检查 DevTools 是否打开。你可以使用 window.onresize 事件来检测窗口大小的变化,有时候 DevTools 打开会导致窗口尺寸变化。
2. 利用 debugger 语句和 try...catch 来检测。当 DevTools 打开时,debugger 语句会触发断点。
3. 使用 MutationObserver 来监听开发者工具的打开。开发者工具通常会改变 DOM 结构,可以通过观察这些变化来判断。
下面是一个综合了这些方法的示例代码:
注意:这种方法也不是绝对安全的,高级用户仍然可能找到绕过的方式。不过,它比单纯的 console.log 检测要更有效一些。另外,确保这些检测不会过度干扰用户体验,毕竟大多数用户是无辜的。
先说你那段代码的问题。那个
/./.test(/./)的检测方式本身就不靠谱,浏览器版本更新后很容易失效,而且这种定时器检测本身就会带来性能开销。更关键的是,一旦你在代码里写死"检测到调试就崩溃",那这个逻辑本身就成了可以被逆向的弱点——攻击者只需要hook掉你的检测函数就行。
我的建议是换个思路:不要试图阻止调试,而是让代码被调试的价值降到最低。
具体做法:
1. 代码层面:用webpack + terser做混淆是OK的,但别在代码里留明显的反调试逻辑。把核心逻辑放到WebAssembly或者用asm.js写,这样逆向难度会高很多。
2. 动态加载:关键代码用eval或者new Function动态生成,代码不在HTML文件里明着写。比如:
3. 如果非要检测调试行为,可以把检测逻辑藏得深一点,并且用更隐蔽的方式,比如检测performance.now()的时间差、或者检测某些DOM属性的访问:
4. 最重要的一点:别让页面直接崩掉。你可以用更隐蔽的方式,比如在检测到异常时让某些功能随机失效、或者返回错误的数据,而不是直接白屏。这样用户不会立刻意识到你在反调试。
5. 考虑加个域名绑定,把关键逻辑放在服务端,前端只做展示。H5游戏本身就应该把核心逻辑放后端,前端只负责渲染。
说白了,前端安全的上限就那么高,再怎么防也挡不住有心的攻击者。你要做的是提高破解成本,而不是试图完全阻止调试。把精力放在游戏本身的可玩性上,比纠结这个划算多了。