前端的代码包括js文件、html文件和css文件都是需要发送给浏览器客户端的,在一定程度上,我们的代码是完全“开源”给了用户,但难免有恶意用户通过调试我们的代码发现系统的漏洞,从而对我们的网站进行攻击,或者盗用我们的代码,与我们进行竞争。这时就需要采取一定的手段,禁止恶意用户盗取代码的行为,或者加大恶意用户使用调试我们代码的成本。现如今,已经有多种成熟的方案可以达到这些目的,比如压缩代码、混淆代码、控制流平坦化等。
这里我们提一种禁止开发者工具调试的方法,基本原理就是让开发者工具一直处于断点调试的状态,从而无法查看真正的代码。
说到调试,首先我们能想到一个关键字 debugger
,这个关键字能让页面在打开开发者工具后自动添加断点,在不打开开发者工具时对用户完全无感知。它本意是为了方便调试,这里,我们使用它来达到一直断点的目的。
(以下代码均以 macOS 10.15.7 系统 Chrome 96.0.4664.55 浏览器执行结果为准)
1 | debugger |
在代码中添加 debugger
可以让调试中断,但是只能中断一次,用户只要按 F8 或者 Command + \ 继续执行脚本,就可以跳过。为了能不停的 debug 我们需要和定时器 setInterval
结合来使用。
1 | setInterval(function(){debugger}, 100); |
这样,用户在打开网页之后就会不停的 debug 。但是,我们总要把上述代码添加到我们自己的代码中去,因为执行到断点时会显示当前脚本所在文件,所以我们的代码还是展示在了用户面前,为了避免这个问题我们可以使用 Function
构造一个新函数。
1 | setInterval(new Function('', 'debugger'), 100); |
这样,在执行到断点的时候,开发者工具就会打开一个名为类似 VM5419 的文件执行断点
1 | (function anonymous( |
但是,有经验的开发者仍旧可以跳过这个断点,只需要在 debugger
所在的行左边点击右键,然后选择“一律不在此处暂停”即可。为了避免这个问题,我们需要每次执行断点时,都创建一个新的匿名函数。
1 | setInterval(function(){new Function('','debugger')()}, 100); |
这样,每次执行断点都会在一个新的匿名函数中,用户就无法通过禁用单次断点来调试我们的代码了。如果用户还是想查看代码,可以通过按 Command + F8 禁用断点来查看。但禁用了断点的话,用户同时也无法调试代码了。你可能想到,即使这样,用户还是可以通过在代码中进行字符串查找,查找 debugger
关键字,然后通过代理将该行代码删除后再进行调试。这里我们就需要让用户找不到 debugger
关键字,方法有很多种,比如改变编码形式,进行字符串拼接等,这里我们以采用 base64 编码的形式为例。
1 | eval(atob('c2V0SW50ZXJ2YWwoZnVuY3Rpb24oKXtuZXcgRnVuY3Rpb24oIiIsImRlYnVnZ2VyIikoKX0sIDEwMCk=')); |
结合 webpack UglifyJS Tensor 等工具,对代码进行拆分,压缩,混淆,监听鼠标键盘按键,对敏感操作(右键 f12 复制)进行一些过滤,重写 console
等日志打印方法,同时加上 无限debugger,可以成倍的加大破解代码的成本,没有能力和耐心的黑客恐怕要知难而退了。