您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > javascript

JS代码安全之路:用JS对JS代码混淆加密

时间:2021-09-03 10:39:29  来源:  作者:修丹道的程序猿

作者:JShaman.com:w2sft

内容预告:

本文将实例讲解以下JS代码混淆加密技术:

方法名转义和转码、成员表达式转IIFE、函数标准化、数值混淆、布尔型常量值混淆、二进制表达式转为调用表达式、字符串转Unicode、局部变量变形、屏蔽输出语句,以及:无限断点、时间差检测等反调试方案。

大纲:

理论层面:为什么要对JS代码进行混淆加密?

技术层面:用JS编程实现对JS代码混淆加密。

防逆向措施:检测与对抗。

专业的混淆加密:JShaman。

彩蛋:字节码加密技术。

理论层面:为什么要对JS代码进行混淆加密?

1、问:JS代码需要考虑安全性吗?

答:当然。

2、问:为什么?

答:JS因为应用环境需要,功能设计目的等历史原因,成为了一种代码公开透明的语言。

前端JS代码,直接暴露在浏览器中,任何访问者,都可以随意查看代码。这就导致代码可以被分析、复制、盗用等,进而引发安全问题,如被利用代码bug攻击、揭露功能逻辑、复制出雷同应用等等。

互联网早些年,安全场景如上。而发展到当下,JS的应用范围更加广泛,如NodeJS的兴起,使很多后端服务、产品、项目也应用了JS。

在后端的角度,如果项目或产品,提交给第三方时,是否要交出源码?显然不妥。

假设服务器被入侵,如果部署的后端服务产品源码也是JS明文,那将导致更严重的安全问题。

更多的应用领域,如小程序开发、H5应用,含ThreeJS引擎类游戏,等,都广泛应用了JS。

在所有这些场景中,都不应该忽视JS代码的安全问题,都应该且需要对JS代码进行保护。

3、问:如何让JS代码变的安全?

答:对JS代码进行保护:混淆&加密,使代码不可读。即:它人依然可以看到代码,但看到的是加密的代码、无法理解代码,更无法修改。

深入并精准的说:通过混淆加密,使代码变的难以阅读和理解。可能有人说,混淆后机器能执行,人就能理解,只是需要的时间长短问题。这种极端的说法,从理论上来说没错,如果可以投入足够长的时间,程序员甚至可以直接用0101写代码。而从实际角度而言,一段代码如果保护后分析需要的时长,超过开发需要的时长,保护的目的就达到了,就会劝退99.9999%对它有想法的正常人类。

理论已探讨完毕,接下来步入正题,探索如何对JS代码进行混淆加密,可不仅仅是应用层面,而是全面掌握:会用、知其然,知其所在然,还要动手编码,实现:用JS对JS代码混淆加密。

接下来的内容,将在NodeJS环境中,使用JS编程,实现对JS代码的混淆加密。

技术层面:用JS编程实现对JS代码混淆加密。

技术理论:如何实现?

确定实现方案之前,首先需要排除几种不可用方案:

  • Eval思路不可用:可以被下断点调试或API HOOK,而轻松还原出原始代码。
  • 可逆加密方式不可用:加密方式可逆,则必然有解密函数,只需定位于解密出口,即可得到原始代码。
  • 异步代码获取并执行不可用:同样可被调试或hook,得到代码。
  • 可取的方式:代码混淆+数据加密。

混淆原理:非replace或regexp方式字符串替换,而是对JS源码进行重编译。从源码,进行词法分析、语法分析、得到AST(抽象语法树),此处是重点,得到AST后,在AST中执行关键混淆加密操作,如:字符算阵列化、字符加密、平展控制流、僵尸代码值入、反调试埋雷、花指令插入等,最后,再将AST重建为JS代码。这样就得到了一份被更改的面目全非的安全JS代码:不可读、不可理解、不可修改、不可还原。

编程现实:用JS对JS代码混淆加密。

由以上的理论可知,重点是混淆加密,而入口点及整体流程框架是AST操作。

JS代码&AST。

在JS引擎之下,代码编译执行大体流程是:

JS代码→AST(抽象语法树)→ByteCode(字节码)→机器码→解释器→执行。

AST设计之初并不是用于对JS代码混淆加密,但AST却很适合这个事情。

基于AST的JS代码混淆加密大体流程:

JS代码→AST→(基于AST的混淆加密)→JS代码。

题外话:能在ByteCode阶段进行加密吗?某些情况下可以,比如NodeJS环境中的JS代码,可以编译为ByteCode。但在前端运行的JS代码,且于DOM有交互的则不理想,小总结而言,有将JS代码进行VM式的加密方法,但通用性较差,使用起来复杂。因为这些弊端,因此,不是普遍性的JS代码保护方案。

注:在本结尾,会有一个彩蛋内容,实例介绍NodeJS字节码生成及运行。

图1,NodeJS字节码效果:

JS代码安全之路:用JS对JS代码混淆加密

 

回到正题,JS代码如何转化为AST?

其实,没有想象中那么复杂。得益于NodeJS成熟的生态,已经有多个已实现模块可以完成这一操作。比较流行的如:esprima、babel,都可以实现对JS代码进行词法分析、语法分析、生成AST、AST操作、从AST再生成JS代码。

用esprima进行JS代码混淆加密。程序框架。

图2、esprima框架demo:

JS代码安全之路:用JS对JS代码混淆加密

 

如图2所示,使用esprima进行JS代码保护的原始功能框架。

代码介绍:

Esprima实现将JS代码转化为AST;

estraverse对AST节点进行遍历,混淆加密的逻辑操作都将在此环节实现;

escodegen则是将操作后的AST转为JS代码输出。

此demo代码未对AST进行任何处理,所以图中右侧的执行结果中可以看到,输出的JS代码与最初代码完全一致。

AST是这样子的。

前面已经对AST进行了说明,AST具体是什么样?

一个方便的办法,是使用astexplorer.net,可以对输入的代码的AST即时同步显示:

图3、const a=1的AST:

JS代码安全之路:用JS对JS代码混淆加密

 

Demo中使用的一行JS语句:“const a=1”,其AST即如图中所显示。

AST是一个JSON结构。

Program表示程序,子节点body中,是变量定义kind是“const”,字面量是“a”,值是“1”。

看似杂乱,但很规整,细看便不难理解。

demo程序里,在节点操作处可以用console输出AST,与astexplorer输出一至,不过前者更方便些。

图4、在程序中输出AST:

JS代码安全之路:用JS对JS代码混淆加密

 

借助Esprima修改AST实例:改“==”为“===”。

图5:

JS代码安全之路:用JS对JS代码混淆加密

 

代码如上图,这是一个很简单的示例。

程序中,estraverse对示例代码结点进行处理,当匹配到“==”时,改为“===”。

为了明确修改节点细节,再对前后代码进行分析。由图6、图7看到,差异仅在节点中的operator。

图6、代码中使用“==”:

JS代码安全之路:用JS对JS代码混淆加密

 

图7、代码中使用“===”:

JS代码安全之路:用JS对JS代码混淆加密

 

借助Esprima修改AST实例:把parseInt改为标准语法。

parseInt方法,有两个参数,参数一是要转化的值,参数二是可选择项,是要转化的进制类型。

图8:

JS代码安全之路:用JS对JS代码混淆加密

 

通过astexplorer,先了解parseInt的AST,未使用参数二时,AST如下:

图9:

JS代码安全之路:用JS对JS代码混淆加密

 

如果有第二参数,则AST如下:

图10:

JS代码安全之路:用JS对JS代码混淆加密

 

那么,要将parseInt转为标准形式即是要给只有一个参数的调用增加第二参数。

代码及执行结果如下:

图11:

JS代码安全之路:用JS对JS代码混淆加密

 

因为初入手的原因,以上描述较为细致,后续将简化。

方法名转义和转码。

如:console.log转为console[log]形式。

通过在aspexplorer中比较可知,造成语句形式差异的原因是CallExpression成员中computed属性值的不同。

图12:

JS代码安全之路:用JS对JS代码混淆加密

 

那么,只需修改节对应节点的computed属性值即可:

图13:

JS代码安全之路:用JS对JS代码混淆加密

 

而修改的条件,则是判断AST节点是CallExpression。上面的例子中,也是使用相似的条件判断方法,找出要修改内容相对应的AST节点。

再进一步,将方法名转为十六进制字符,console[log]会成为:console['x6cx6fx67'],以此进一步降低代码可读性。

图14、增加字符串转16进制操作:

JS代码安全之路:用JS对JS代码混淆加密

 

例程代码输出为:

图15:

JS代码安全之路:用JS对JS代码混淆加密

 

运行混淆后的代码:

图16:

JS代码安全之路:用JS对JS代码混淆加密

 

从简单的例程,可以初步学习到对AST的操作方法。

接下来,实现一个有点难度的功能。

成员表达式转为IIFE

成员表达式通常指调用对象的成员,例如 console 对象的 log 成员。

IIFE,全称为:Immediately Invoked Function Expression,在JAVAScript编程中,是指:立即调用函数表达式。

为了方便理解,先展示此功能实现后的效果:

图17:

JS代码安全之路:用JS对JS代码混淆加密

 

如上图中,console的log、warn、error方法,以及字符串的toUperCase()方法,在保护后都会成为匿名自执行的函数,且方法名都以数组化的形式被另外存放,代码相比之前混乱了许多。

图18、IIFE代码执行效果:

JS代码安全之路:用JS对JS代码混淆加密

 

实现方法如下:

架构与之前略有差异,traverse方法改为replace,enter事件改为leave事件,如下图:

图19:

JS代码安全之路:用JS对JS代码混淆加密

 

对变量定义结点,如console.log输出的信息,以及成员函数,如console的log方法进行操作。

图20、改写字符串定义、成员函数调用:

JS代码安全之路:用JS对JS代码混淆加密

 

Add_string函数把字符串信息、方法名,都写入到一个新的字符串数组,并且把方法改为IIFE。

字符串数组建立、方法改为IIFE的具体实现如下图:

图21:

JS代码安全之路:用JS对JS代码混淆加密

 

然后,把新增的数组加入到AST中,最重再重建代码:

图22:

JS代码安全之路:用JS对JS代码混淆加密

 

这样就完成了本例功能。

注:本例仅供功能演示,尚有不严谨的逻辑,比如成员方法IIFE化之前,除应该判断node.type为MemberExpression,还应排除节点computed为true的情况,否则代码执行会发生错误。

正如前文中所述,能对AST进行操作的模块不止esprima,babel也是个很好的选择。

接下来的例子,将使用babel来完成。

Babel的使用方式与esprima极为相似,其代码框架如下:

图23:

JS代码安全之路:用JS对JS代码混淆加密

 

同样是:JS代码→AST→节点处理→JS代码。

用Babel修改AST实例:去除代码中的console.log输出语句。

代码如下图所示:

图24、用Babel在AST中去除console.log节点:

JS代码安全之路:用JS对JS代码混淆加密

 

匹配AST中的成员操作节点,且满足条件callee的对像名为console,属性方法名为log,如检测掉,则remove该节点。

运行效果如下图所示,测试代码中含有console.log,修改后输出中已经被去除。

图25:

JS代码安全之路:用JS对JS代码混淆加密

 

严谨的考虑的话,需要注意对象挂载的识别,如global.console.log,此时remove则会剩下global,将导致语法错误,因此还应该判断父节点类型来排除这种情况。

指定局部变量变形

图26、对min、number两个局部变量变形:

JS代码安全之路:用JS对JS代码混淆加密

 

相当于是可设定、可配置的对某些变量进行变形。

反向思考,也可以排除对某些变量的处理,等同于白名单,类似于JShaman平台中的“保留字”功能。

删除代码中的空行。

图27:

JS代码安全之路:用JS对JS代码混淆加密

 

EmptyStatement表示空语句AST节点。

字符串转Unicode。

图28:

JS代码安全之路:用JS对JS代码混淆加密

 

代码及执行结果如上图,原理为:判断节字符串字面量节点是否为Unicode格式,如不是则转为Unicode。

在这几个例子中,可看到与esprima的差异,esprima使用的是enter、leave方法,Babel中是直接对要处理的节点类型操作,如上图中的StringLiteral。

更条理化的写法,上面的代码可以修改如下,这个方法被称为Babel-plugin(插件):

图29:

JS代码安全之路:用JS对JS代码混淆加密

 

二进制表达式转为调用表达式

即BinaryExpression节点转为CallExpression。

先看效果:

图30,左侧为二进制表达式,右侧为调用表达式:

JS代码安全之路:用JS对JS代码混淆加密

 

二进制表达式AST形式:

JS代码安全之路:用JS对JS代码混淆加密

 

实现代码:

图31:

JS代码安全之路:用JS对JS代码混淆加密

 

调用表达式AST形式:

图32:

JS代码安全之路:用JS对JS代码混淆加密

 

代码中的这部分,即是将二进制表达式转化为调用表达式:

图33:

JS代码安全之路:用JS对JS代码混淆加密

 

布尔型常量值混淆

代码及效果如下图:

图34:

JS代码安全之路:用JS对JS代码混淆加密

 

数值混淆

代码及效果如下图:

图35:

JS代码安全之路:用JS对JS代码混淆加密

 

JS代码混淆加密,虽不至博大精深,但也属于高段位技术。

在此分享部分浅显方案,以展现其实用效果,用于说明混淆加密手段对于JS代码加固的有效性。此外,还有更多高端的防护手段,如JShaman应用的:平展控制流、时间限制、域名锁定、僵尸代码植入等。

图36、JShaman的JS代码保护配置功能:

JS代码安全之路:用JS对JS代码混淆加密

 

防逆向措施:检测与对抗。

对JS代码进行混淆加密之后,代码安全度得到相当的加强,但还能更进一步,为了防止不法者进行逆向分析、破解,可在代码中加入防破解对抗功能。这也是被JShaman应用的方案。

  • 无限断点。

JS当中有一个debugger指令,当处于调试工具中时,如在浏览器中,会形成断点,使调试中断,利用此特性,在程序中加入无限的debugger,可使代码无法被调试。

图37、每100毫秒一个断点:

JS代码安全之路:用JS对JS代码混淆加密

 

浏览器中执行效果如下,当打开“调试器”时,程序会不停的中断,导致无法跟踪代码:

图38:

JS代码安全之路:用JS对JS代码混淆加密

 

时间差检测。

代码及执行效果如下图所示:

图39:

JS代码安全之路:用JS对JS代码混淆加密

 

检测原理是:

在代码中加入console.log输出和console.clear语句,未在调试工具中时,这两句代码执行是不需渲染显示的,执行耗时短,但假如在浏览器中打开了开发者工具,则会因为显示输出并清除的操作而消耗较多时间,这会被程序察觉出耗时异常,从而检测出是在被调试。

专业的混淆加密:JShaman

本文讲述了部分JS代码混淆加密技术及实现,更多更专业的防护方案未有尽述,这里再展示一段经JShaman保护的代码,领略专业级的JS代码安全。

图40、测试代码准备进行混淆加密:

JS代码安全之路:用JS对JS代码混淆加密

 

图41、保护选项设置:

JS代码安全之路:用JS对JS代码混淆加密

 

图42、混淆加密后的JS代码:

JS代码安全之路:用JS对JS代码混淆加密

 

彩蛋:字节码加密技术。

提示:JS字节码(ByteCode)加密技术,理论可行,但通用性较差,在此仅做技术介绍,不推荐做为项目或产品正式使用方案。

  • 字节码生成

在NodeJS中将JS代码生成字节码,方法很简单,需借助google的V8引擎,V8引擎内置有JS虚拟机。通过v8虚拟机,将JS代码编译为字节码。全程仅需十几行代码,如下图:

图43:

JS代码安全之路:用JS对JS代码混淆加密

 

关键处是cachedData,即字节码。

  • 运行字节码

V8虚拟机是能够识别和直接运行该字节码的。

代码如下,如同创建字节码一样简单。

图44:

JS代码安全之路:用JS对JS代码混淆加密

 

生成的字节码是非文本形式的,如强行打开,内容如下图:

图45、字节码文件内容:

JS代码安全之路:用JS对JS代码混淆加密

 

JS字节码生成并运行效果如下:

图46:

JS代码安全之路:用JS对JS代码混淆加密

 

代码改变世界,献给广大JS开发者。全文结束,感谢阅读。



Tags:JS   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
前言JDBC访问Postgresql的jsonb类型字段当然可以使用Postgresql jdbc驱动中提供的PGobject,但是这样在需要兼容多种数据库的系统开发中显得不那么通用,需要特殊处理。本文介绍...【详细内容】
2021-12-23  Tags: JS  点击:(12)  评论:(0)  加入收藏
1. 检测一个对象是不是纯对象,检测数据类型// 检测数据类型的方法封装(function () { var getProto = Object.getPrototypeOf; // 获取实列的原型对象。 var class2type =...【详细内容】
2021-12-08  Tags: JS  点击:(23)  评论:(0)  加入收藏
前言前几天有粉丝在群里问了一个json文件处理的问题。看上去他只需要follower和ddate这两个字段下的对应的值。我们知道json是一种常见的数据传输形式,所以对于爬取数据的数...【详细内容】
2021-12-07  Tags: JS  点击:(40)  评论:(0)  加入收藏
作者:前端进阶者来源:前端进阶学习交流一、前言 我们经常在网页上 ,游戏界面加载时会看到加载进度条的效果,我们往往会以为这些加载进度条的效果,很难实现。今天教大家JS+CSS结合...【详细内容】
2021-11-05  Tags: JS  点击:(45)  评论:(0)  加入收藏
今天我们将尝试下花 1 分钟的时间简单地了解下什么是 JS 代理对象(proxies)?我们可以这样理解,JS 代理就相当于在对象的外层加了一层拦截,在拦截方法里我们可以自定义一些个性化...【详细内容】
2021-10-18  Tags: JS  点击:(51)  评论:(0)  加入收藏
带有多个条件的 if 语句把多个值放在一个数组中,然后调用数组的 includes 方法。// bad if (x === "abc" || x === "def" || x === "ghi" || x === "jkl") { //logic } // be...【详细内容】
2021-09-27  Tags: JS  点击:(58)  评论:(0)  加入收藏
利用JS的CryptoJS 3.x和PHP的openssl_encrypt,openssl_decrypt实现AES对称加密解密,由于需要两种语言对同一字符串的操作,而CryptoJS 的默认加密方式为“aes-256-cbc”,PHP端也...【详细内容】
2021-09-16  Tags: JS  点击:(79)  评论:(0)  加入收藏
作者:JShaman.com:w2sft内容预告:本文将实例讲解以下JS代码混淆加密技术:方法名转义和转码、成员表达式转IIFE、函数标准化、数值混淆、布尔型常量值混淆、二进制表达式转为调用...【详细内容】
2021-09-03  Tags: JS  点击:(81)  评论:(0)  加入收藏
Web 浏览器日益强大,网站和 Web 应用程序的复杂性也在增加。几十年前需要超级计算机的操作现在可以在智能手机上运行,其中之一就是人脸检测。检测和分析人脸的能力非常有用,因...【详细内容】
2021-08-20  Tags: JS  点击:(105)  评论:(0)  加入收藏
shell脚本是一个命令语言,面向的是操作系统执行。如果写过shell脚本的话,应该体会过编写过程的痛苦。因为shell并不是一个编程语言,并不支持常见的数组,JSON等数据结构,也不支持...【详细内容】
2021-08-09  Tags: JS  点击:(109)  评论:(0)  加入收藏
▌简易百科推荐
1、通过条件判断给变量赋值布尔值的正确姿势// badif (a === 'a') { b = true} else { b = false}// goodb = a === 'a'2、在if中判断数组长度不为零...【详细内容】
2021-12-24  Mason程    Tags:JavaScript   点击:(5)  评论:(0)  加入收藏
给新手朋友分享我收藏的前端必备javascript已经写好的封装好的方法函数,直接可用。方法函数总计:41个;以下给大家介绍有35个,需要整体文档的朋友私信我,1、输入一个值,将其返回数...【详细内容】
2021-12-15  未来讲IT    Tags:JavaScript   点击:(19)  评论:(0)  加入收藏
1. 检测一个对象是不是纯对象,检测数据类型// 检测数据类型的方法封装(function () { var getProto = Object.getPrototypeOf; // 获取实列的原型对象。 var class2type =...【详细内容】
2021-12-08  前端明明    Tags:js   点击:(23)  评论:(0)  加入收藏
作者:一川来源:前端万有引力 1 写在前面Javascript中的apply、call、bind方法是前端代码开发中相当重要的概念,并且与this的指向密切相关。本篇文章我们将深入探讨这个关键词的...【详细内容】
2021-12-06  Nodejs开发    Tags:Javascript   点击:(18)  评论:(0)  加入收藏
概述DOM全称Document Object Model,即文档对象模型。是HTML和XML文档的编程接口,DOM将文档(HTML或XML)描绘成一个多节点构成的结构。使用JavaScript可以改变文档的结构、样式和...【详细内容】
2021-11-16  海人为记    Tags:DOM模型   点击:(34)  评论:(0)  加入收藏
入口函数 /*js加载完成事件*/ window.onload=function(){ console.log("页面和资源完全加载完毕"); } /*jQuery的ready函数*/ $(document).ready(function(){ co...【详细内容】
2021-11-12  codercyh的开发日记    Tags:jQuery   点击:(35)  评论:(0)  加入收藏
一、判断是否IE浏览器(支持判断IE11与edge)function IEVersion() {var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串var isIE = userAgent.indexOf("comp...【详细内容】
2021-11-02  V面包V    Tags:Javascript   点击:(39)  评论:(0)  加入收藏
Null、Undefined、空检查普通写法: if (username1 !== null || username1 !== undefined || username1 !== '') { let username = username1; }优化后...【详细内容】
2021-10-28  前端掘金    Tags:JavaScript   点击:(50)  评论:(0)  加入收藏
今天我们将尝试下花 1 分钟的时间简单地了解下什么是 JS 代理对象(proxies)?我们可以这样理解,JS 代理就相当于在对象的外层加了一层拦截,在拦截方法里我们可以自定义一些个性化...【详细内容】
2021-10-18  前端达人    Tags:JS   点击:(51)  评论:(0)  加入收藏
带有多个条件的 if 语句把多个值放在一个数组中,然后调用数组的 includes 方法。// bad if (x === "abc" || x === "def" || x === "ghi" || x === "jkl") { //logic } // be...【详细内容】
2021-09-27  羲和时代    Tags:JS   点击:(58)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条