华为做网站,免费签名设计在线生成,x浏览器,宣城市住房和城乡建设局网站作者#xff1a;vivo 互联网安全团队- Luo Bingsong 前端代码都是公开的#xff0c;为了提高代码的破解成本、保证JS代码里的一些重要逻辑不被居心叵测的人利用#xff0c;需要使用一些加密和混淆的防护手段。
一、概念解析
1.1 什么是接口加密
如今这个时代#xff0c;… 作者vivo 互联网安全团队- Luo Bingsong 前端代码都是公开的为了提高代码的破解成本、保证JS代码里的一些重要逻辑不被居心叵测的人利用需要使用一些加密和混淆的防护手段。
一、概念解析
1.1 什么是接口加密
如今这个时代数据已经变得越来越重要网页和APP是主流的数据载体如果获取数据的接口没有设置任何的保护措施的话数据就会被轻易地窃取或篡改。
除了数据泄露外一些重要功能的接口如果没有做好保护措施也会被恶意调用造成DDoS、条件竞争等攻击效果比如如下几个场景 一些营销活动类的Web页面领红包、领券、投票、抽奖等活动方式很常见。此类活动对于普通用户来说应该是“拼手气”而对于非正常用户来说可以通过直接刷活动API接口的这种“作弊”方式来提升“手气”。这样对普通用户来说就很不公平。
所以对重要接口都会采用加密验签的方式进行保护而验签的加密逻辑大多数都通过JS代码实现所以保护JS代码不被攻击者窃取尤为重要。
1.2 为什么要保护JS代码 JavaScript代码运行于客户端 JavaScript代码是公开透明的
由于这两个原因致使JavaScript代码是不安全的任何人都可以读、分析、复制、盗用甚至篡改。
1.3 应用场景
以下场景就通过特定的防护措施提高了攻击成本 某些网站会在页面中使用JavaScript对数据进行加密以保护数据的安全性和隐私性在爬取时需要通过解密JavaScript代码才能获取到数据。 某些网站的URL会有某个参数带有一些看不太懂的长串加密参数攻击者要爬取的话就必须要知道这些参数是怎么构造的否则无法正确地访问该URL。 翻看网站的JavaScript源代码可以发现很多压缩了或者看不太懂的字符比如JavaScript文件名被编码JavaScript的文件内容都压缩成几行JavaScript变量也被修改成单个字符或者一些十六进制的字符所以我们不能轻易地根据JavaScript找出某些接口的加密逻辑。
1.4 涉及的技术
这些场景都是网站为了保护数据不被轻易抓取采取的措施运用的技术主要有 接口加密技术 JavaScript压缩、混淆和加密技术
二、技术原理
2.1 接口加密技术
数据和功能一般是通过服务器提供的接口来实现为了提升接口的安全性客户端会和服务端约定一种接口检验方式通常是各种加密和编码算法如Base64、Hex、MD5、AES、DES、RSA等。
常用的数据接口都会携带一个sign参数用于权限管控
① 客户端和服务端约定一种接口校验逻辑客户端在每次请求服务端接口的时候附带一个sign参数。② sign参数的逻辑自定义可以由当前时间戳信息、设备ID、日期、双方约定好的秘钥经过一些加密算法构造而成。③ 客户端根据约定的加密算法构造sign每次请求服务器的时候附带上sign数。④ 服务端根据约定的加密算法和请求的数据对sign进行校验如果检验通过才返回数据否则拒绝响应。 这就是一个比较简单的接口参数加密的实现如果有人想要调用这个接口的话必须要破解sign的生成逻辑否则是无法正常调用接口的。
当然上面的实现思路比较简单还可以增加一些时间戳信息和访问频次来增加时效性判断或使用非对称加密提高加密的复杂程度。
实现接口参数加密需要用到一些加密算法客户端和服务器都有对应的SDK来实现这些加密算法如JavaScript的crypto-js、Python的hashlib、Crypto等等。如果是网页且客户端的加密逻辑是用JavaScript来实现的话其源代码对用户是完全可见的所以我们需要用压缩、混淆、加密的方式来对JavaScript代码进行一定程度的保护。
2.2 什么是压缩
去除JavaScript代码中不必要的空格、换行等内容使源码都压缩为几行内容降低代码可读性同时可提高网站的加载速度。
如果仅仅是去除空格换行这样的压缩方式几乎没有任何防护作用这种压缩方式仅仅是降低了代码的直接可读性可以用IDE、在线工具或Chrome轻松将JavaScript代码变得易读。
所以JavaScript压缩技术只能在很小的程度上起到防护作用想提高防护的效果还得依靠JavaScript混淆和加密技术。
2.3 什么是混淆
使用变量混淆、字符串混淆、属性加密、控制流平坦化、调试保护、多态变异等手段使代码变得难以阅读和分析同时不影响代码原有功能是一种理想且实用的JS保护方案。 变量混淆将变量名、方法名、常量名随机变为无意义的乱码字符串降低代码可读性如转成单个字符或十六进制字符串。 字符串混淆将字符串阵列化集中放置并进行MD5或Base64编码存储使代码中不出现明文字符串可以避免使用全局搜索字符串的方式定位到入口点。 属性加密针对JavaScript对象的属性进行加密转化隐藏代码之间的调用关系把key-value的映射关系混淆掉。 控制流平坦化打乱函数原有代码执行流程及函数调用关系使代码逻辑变得混乱无序。 调试保护基于调试器特性加入一些强制调试debug语句无限debug、定时debug、debug关键字使其在调试模式下难以顺利执行JavaScript代码。 多态变异JavaScript代码每次被调用时代码自身立刻自动发生变异变化为与之前完全不同的代码避免代码被动态分析调试。
2.4 什么是加密
JavaScript加密是对JavaScript混淆技术防护的进一步升级基本思路是将一些核心逻辑用C/C语言来编写并通过JavaScript调用执行从而起到二进制级别的防护作用加密的方式主要有Emscripten和WebAssembly等。
1. Emscripten
Emscripten编译器可以将C/C代码编译成asm.js的JavaScript变体再由JavaScript调用执行因此某些JavaScript的核心功能可以使用C/C语言实现。
2.WebAssembly
WebAssembly也能将C/C代码转成JavaScript引擎可以运行的代码但转出来的代码是二进制字节码而asm.js是文本因此运行速度更快、体积更小得到的字节码具有和JavaScript相同的功能在语法上完全脱离JavaScript同时具有沙盒化的执行环境利用WebAssembly技术可以将一些核心的功能用C/C语言实现形成浏览器字节码的形式然后在JavaScript中通过类似如下的方式调用 这种加密方式更加安全想要逆向或破解需要逆向WebAssembly难度极大。
2.5 工具介绍
2.5.1 压缩混淆工具 Uglifyjs开源
用NodeJS编写的JavaScript压缩工具是目前最流行的JS压缩工具JQuery就是使用此工具压缩UglifyJS压缩率高压缩选项多并且具有优化代码格式化代码功能。 jshaman
jshaman是一个商业级工具看了很多社区的评论这个目前是最好的可以在线免费使用也可以购买商业版。 jsfuck
开源的js混淆工具原理比较简单通过特定的字符串加上下标定位字符再由这些字符替换源代码从而实现混淆。 YUI Compressor
业界巨头yahoo提供的一个前端压缩工具通过java库编译css或js文件进行压缩
2.5.2 反混淆工具 jsbeautifier
jsbeautifier是一个为前端开发人员制作的Chrome扩展能够直接查看经过压缩的Javascript代码。 UnuglifyJS
压缩工具uglify对应的解混淆工具。 jspacker
用PHP编写的压缩工具可以混淆代码保护知识产权产生的代码兼容IE、FireFox等常用浏览器国内大部分在线工具网站都采用这种算法压缩。
三、前端安全对抗
3.1 前端调试手法
3.1.1 Elements
Elements 面板会显示目前网页中的 DOM、CSS 状态且可以修改页面上的 DOM 和 CSS即时看到结果省去了在编辑器修改、储存、浏览器查看结果的流程。
有时候一些dom节点会嵌套很深导致我们很难利用Element面板html代码来找到对应的节点。inspect(dom元素)可以让我们快速跳转到对应的dom节点的html代码上。 3.1.2 Console
Console对象提供了浏览器控制台调试的接口Console是一个对象上面有很多方便的方法。 console.log( )最常用的语句可以将变量输出到浏览器的控制台中方便开发者调用JS代码 console.table( )可用于打印obj/arr成表格 console.trace( )可用于debugger堆栈调试方便查看代码的执行逻辑看一些库的源码 console.count( )打印标签被执行了几次预设值是default可用在快速计数 console.countReset( )用来重置可用在计算单次行为的触发的计数 console.group( )/console.groupEnd( )为了方便一眼看到自己的log可以用console.group自定义message group标签还可以多层嵌套并用console.groupEnd来关闭Group。
3.1.3 JS断点调试
JS断点调试即在浏览器开发者工具中为JS代码添加断点让JS执行到某一特定位置停住方便开发者对该处代码段进行分析与逻辑处理。 Sources面板
① 普通断点breakpoint
给一段代码添加断点的流程是F12Ctrl Shift I打开开发工具-点击Sources菜单-左侧树中找到相应文件→点击行号列即完成在当前行添加/删除断点操作。当断点添加完毕后刷新页面JS执行到断点位置停住在Sources界面会看到当前作用域中所有变量和值。 恢复Resume 恢复按钮(第一个按钮)继续执行快捷键 F8继续执行如果没有其他的断点那么程序就会继续执行并且调试器不会再控制程序。 跨步Step over运行下一条指令但不会进入到一个函数中快捷键 F10。 步入Step into快捷键 F11和“下一步Step”类似但在异步函数调用情况下表现不同步入会进入到代码中并等待异步函数执行。 步出Step out继续执行到当前函数的末尾快捷键 ShiftF11继续执行代码并停止在当前函数的最后一行当我们使用偶然地进入到一个嵌套调用但是我们又对这个函数不感兴趣时我们想要尽可能的继续执行到最后的时候是非常方便的。 下一步Step运行下一条语句快捷键 F9一次接一次地点击此按钮整个脚本的所有语句会被逐个执行下一步命令会忽略异步行为。
启用/禁用所有的断点这个按钮不会影响程序的执行。只是一个批量操作断点的开/关。 察看Watch显示任意表达式的当前值 调用栈Call Stack显示嵌套的调用链 作用域Scope显示当前的变量 Local显示当前函数中的变量 Global显示全局变量
② 条件断点Conditional breakpoint
给断点添加条件只有符合条件时才会触发断点条件断点的颜色是橙色。 ③ 日志断点logpoint
当代码执行到这里时会在控制台输出你的表达式不会暂停代码执行日志断点式粉红色。 debugger命令
通过在代码中添加debugger;语句当代码执行到该语句的时候就会自动断点之后的操作和在Sources面板添加断点调试唯一的区别在于调试完后需要删除该语句。
在开发中偶尔会遇到异步加载html片段包含内嵌JS代码的情况而这部分JS代码在Sources树中无法找到因此无法直接在开发工具中直接添加断点那么如果想给异步加载的脚本添加断点此时debugger;就发挥作用了。
3.2 反调试手段
3.2.1 禁用开发者工具
监听是否打开开发者工具若打开则直接调用JavaScript的window.close( )方法关闭网页
① 监听F12按键、监听CtrlShiftIWindows系统组合键、监听右键菜单监听Ctrls禁止保存至本地避免被Overrides。
script//监听F12、CtrlShifti、Ctrlsdocument.onkeydown function (event) {if (event.key F12) {window.close();window.location about:blank;} else if (event.ctrlKey event.shiftKey event.key I) {//此处I必须大写window.close();window.location about:blank;} else if (event.ctrlKey event.key s) {//此处s必须小写event.preventDefault();window.close();window.location about:blank;}};//监听右键菜单document.oncontextmenu function () {window.close();window.location about:blank;};
/script
② 监听窗口大小变化
scriptvar h window.innerHeight, w window.innerWidth;window.onresize function () {if (h ! window.innerHeight || w ! window.innerWidth) {window.close();window.location about:blank;}}
/script
③ 利用Console.log
script//控制台打开的时候回调方法function consoleOpenCallback(){window.close();window.location about:blank;return ;}//立即运行函数用来检测控制台是否打开!function () {// 创建一个对象let foo /./;// 将其打印到控制台上实际上是一个指针console.log(foo);// 要在第一次打印完之后再重写toString方法foo.toString consoleOpenCallback;}()
/script
3.2.2 无限debugger反调试
① constructor
scriptfunction consoleOpenCallback() {window.close();window.location about:blank;}setInterval(function () {const before new Date();(function(){}).constructor(debugger)();// debugger;const after new Date();const cost after.getTime() - before.getTime();if (cost 100) {consoleOpenCallback();}}, 1000);
/script
② Function
scriptsetInterval(function () {const before new Date();(function (a) {return (function (a) {return (Function(Function(arguments[0] a )()))})(a)})(bugger)(de);// Function(debugger)();// debugger;const after new Date();const cost after.getTime() - before.getTime();if (cost 100) {consoleOpenCallback2();}}, 1000);
/script
有大佬写了一个库专门用来判断是否打开了开发者工具可供参考使用点击查看
3.3 反反调试手段
3.3.1 禁用开发者工具
针对判断是否打开开发者工具的破解方式很简单只需两步就可以搞定。
① 将开发者工具以独立窗口形式打开
② 打开开发者工具后再打开网址
3.3.2 无限debugger
针对无限debugger反调试有以下破解方法
① 直接使用dubbger指令的可以在Chrome找到对应行格式化后右键行号选择Never pause here即可。
② 使用了constructor构造debugger的只需在console中输入以下代码后点击F8Resume script execution回复js代码执行即可直接点击小的蓝色放行按钮即可。
Function.prototype.constructorfunction(){}
③ 使用了Function构造debugger的只需在console中输入以下代码。
Function function () {}
3.4 总结
JavaScript混淆加密使得代码更难以被反编译和分析从而提高了代码的安全性攻击者需要花费更多的时间和精力才能理解和分析代码从而降低了攻击者入侵的成功率但它并不能完全保护代码不被反编译和分析如果攻击者有足够的时间和资源他们仍然可以理解代码并找到其中的漏洞道高一尺魔高一丈任何客户端加密混淆都会被破解只要用心都能解决我们能做的就是拖延被破解的时间所以尽量避免在前端代码中嵌入敏感信息或业务逻辑。