前端 JS 安全对抗原理与实践

作者: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)

给一段代码添加断点的流程是:"F12(Ctrl + Shift + I)打开开发工具"->"点击Sources菜单"->"左侧树中找到相应文件"→"点击行号列"即完成在当前行添加/删除断点操作。当断点添加完毕后,刷新页面JS执行到断点位置停住,在Sources界面会看到当前作用域中所有变量和值。

图片

  • 恢复(Resume): 恢复按钮(第一个按钮),继续执行,快捷键 F8,继续执行,如果没有其他的断点,那么程序就会继续执行,并且调试器不会再控制程序。
  • 跨步(Step over):运行下一条指令,但不会进入到一个函数中,快捷键 F10。

  • 步入(Step into):快捷键 F11,和“下一步(Step)”类似,但在异步函数调用情况下表现不同,步入会进入到代码中并等待异步函数执行。

  • 步出(Step out):继续执行到当前函数的末尾,快捷键 Shift+F11,继续执行代码并停止在当前函数的最后一行,当我们使用偶然地进入到一个嵌套调用,但是我们又对这个函数不感兴趣时,我们想要尽可能的继续执行到最后的时候是非常方便的。

  • 下一步(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按键、监听Ctrl+Shift+I(Windows系统)组合键、监听右键菜单,监听Ctrl+s禁止保存至本地,避免被Overrides。

<script>//监听F12、Ctrl+Shift+i、Ctrl+sdocument.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>

② 监听窗口大小变化

<script>var 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

<script>function 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

<script>setInterval(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中输入以下代码后,点击F8(Resume script execution)回复js代码执行即可(直接点击小的蓝色放行按钮即可)。

Function.prototype.constructor=function(){}

③ 使用了Function构造debugger的,只需在console中输入以下代码。

Function = function () {}

3.4 总结

JavaScript混淆加密使得代码更难以被反编译和分析,从而提高了代码的安全性,攻击者需要花费更多的时间和精力才能理解和分析代码,从而降低了攻击者入侵的成功率,但它并不能完全保护代码不被反编译和分析,如果攻击者有足够的时间和资源,他们仍然可以理解代码并找到其中的漏洞,道高一尺,魔高一丈,任何客户端加密混淆都会被破解,只要用心都能解决,我们能做的就是拖延被破解的时间,所以尽量避免在前端代码中嵌入敏感信息或业务逻辑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/240648.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高德地图逆地理编码踩坑日志

本人是一枚Java小白&#xff0c;公司项目中用到根据经纬度反查该地址中文信息的场景&#xff0c;因为一开始调用的经纬度是能反查出区域编码的&#xff0c;以为towncode都是String返回结果&#xff0c;如下图&#xff1a; 没想到当没有名字任何一个城市区域的时候&#xff0c;…

管理 Jenkins 详细指南

目录 系统配置 安全 状态信息 故障 排除 工具和操作 系统配置 系统&#xff0c;配置全局设置和路径&#xff0c;端口更改&#xff0c;下载地址等。 工具&#xff0c;配置工具、其位置和自动安装程序。 插件&#xff0c;添加、删除、禁用或启用可以扩展 Jenkins 功能的插…

ssh远程管理服务

什么是ssh SSH是一种加密的网络协议&#xff0c;用于在不安全的网络中安全地传输数据。它允许用户通过一个安全的通道连接到远程计算机&#xff0c;并在该通道上执行各种网络服务&#xff0c;例如远程登录和文件传输。 SSH使用公钥加密技术来验证远程计算机的身份&#xff0c;并…

初识Stable Diffusion

界面选项解读 这是在趋动云上部署的Stable Diffusion txt2img prompt &#xff08;1&#xff09;分割符号&#xff1a;使用逗号 , 用于分割词缀&#xff0c;且有一定权重排序功能&#xff0c;逗号前权重高&#xff0c;逗号后权重低 &#xff08;2&#xff09;建议的通用范式…

【C++11特性篇】玩转C++11中的包装器(function&bind)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; 目录 一.为什么需要包装器function&#xff…

【Earth Engine】协同Sentinel-1/2使用随机森林回归实现高分辨率相对财富(贫困)制图

目录 1 简介与摘要2 思路3 效果预览4 代码思路5 完整代码6 后记 1 简介与摘要 最近在做一些课题&#xff0c;需要使用Sentinel-1/2进行机器学习制图。 然后想着总结一下相关数据和方法&#xff0c;就花半小时写了个代码。 然后再花半小时写下这篇博客记录一下。 因为基于多次拍…

通过windows cng api 实现rsa非对称加密

参考&#xff1a; 1,使用 CNG 加密数据 - Win32 apps | Microsoft Learn 2,不记得了 &#xff08;下文通过cng api演示rsa加密&#xff0c;不做原理性介绍&#xff09; 相对于aes等对称加密算法&#xff0c;rsa加密算法不可逆性更强。非对称加密在通常情况下&#xff0c;使…

前端传输formDate格式的数据,后端不能用@RequestBody接收

写了个接口&#xff0c;跟前端对接&#xff0c;前端说怎么一直415的报错 我寻思不对啊&#xff0c;我swagger都请求成功了&#xff0c;后来发现前端一直是以formdata格式提交的数据&#xff0c;这样我其实是可以不加RequestBody的&#xff1b; 知识点&#xff1a; RequestBody…

类和对象

1 类定义&#xff1a; class ChecksumAccumulator {// class definition goes here } 你就能创建 ChecksumAccumulator 对象&#xff1a;new CheckSumAccumulator 注&#xff1a;1scala类中成员默认是public类型&#xff0c;若设为私有属性则必须加private关键字。在scala中是…

基于Springboot的留守儿童爱心网站(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的留守儿童爱心网站(有报告)。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring…

技术交底二维码的应用

二维码技术交底可以逐级落实、责任到人、有据可查、是目前最方便、实用的交底方式&#xff0c;下面我们讲解技术交底二维码的应用。 1、生成对应的技术交底二维码&#xff0c;将施工方案、技术资料、安全教育资料等内容上传到二维码里。打印出来现场粘贴&#xff0c;便于作业班…

java中线程相关的面试题

什么是线程安全&#xff0c;造成线程安全的本质是什么&#xff1f; 什么是线程安全呢&#xff1f; 咱们初步去理解话记住一句话就行&#xff1a;如果一个对象可以安全地被多个线程同时使用&#xff0c;那它就是线程安全的。 为什么并发编程会导致线程不安全&#xff1f; 可见…

用友U8CRM系统help2 任意文件读取漏洞复现

用友U8CRM系统的help2文件中接口存在任意文件读取漏洞&#xff0c;攻击者在未登录情况下即可进行漏洞利用。 1.1 漏洞级别 高危 1.2 快速检索 fofa语法&#xff1a; title"用友U8CRM"1.3 漏洞复现 该漏洞利用非常简单&#xff0c;只需构造get请求 访问该地址即可…

青少年CTF-qsnctf-A1-Misc-签到

题目环境&#xff1a; 题目难度&#xff1a;★题目描述&#xff1a;有没有可能&#xff0c;这个平台就是个题目&#xff1f; 一道杂项题 题目说的是这个平台就是题目 那么也就是说flag就在这个平台里面1.从高层次向低层次逐一排查 2.首先对平台首页进行排查进平台首页 第一种解…

HTML---浮动

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 一.常见的网页布局 二.标准文档流 标准文档流常见标签 标准文档流的组成 块级元素<div…

二叉搜索树 --- C++实现

目录 1.二叉搜索树的概念 2.二叉搜索树的操作 3. 二叉树的实现 4.二叉搜索树的应用 5. 二叉树的性能分析 6. 二叉树进阶练习题 1.二叉搜索树的概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树&#xff1a; 若它的左…

JDBC 知识点总结篇

JDBC 知识点总结篇 JDBC 接口 Java DataBase Connectivity Java数据库连接&#xff0c;由官方定义的一套操作所有关系型数据库的规则&#xff0c;即接口&#xff0c;各个数据库厂商实现该套接口 代码 // 本代码只提供一个样例&#xff0c;请根据自己实际情况修改代码 // 1.…

MyBatis笔记

Mybatis Mybatis介绍 什么是Mybatis? mybatis是支持普通SQL查询、存储过程和高级映射的优秀持久层框架。 Mybatis优点 几乎消除了JDBC代码和参数的手动设置消除结果集的检索使用XML或注解用于配置和原始映射&#xff0c;将接口和POJOs(实体类)映射成数据库中的记录。 My…

vue微乾坤子应用开发及ele组件开发时问题记录

一. 微乾坤 1. 新增page页面路由,pmi权限中心配置正常&#xff0c;跳转链接正确&#xff0c;但路由未找到403. 解决&#xff1a; 新增的配置是page类型&#xff0c;transformQianKunRoute方法转换微前端路由数据 时&#xff0c;过滤未兼容page型的路由&#xff0c; 解决 [menu,…

react中使用redux最简单最方便的方式,配合rematch简化操作,5分钟学会

react中使用状态管理的方式也很多&#xff0c;比如redux和mobx等&#xff0c;今天这一片就讲一下redux的入门到熟练使用&#xff0c;主要是要理解它redux的组成有哪些&#xff0c;到怎么创建&#xff0c;和组建中怎么使用三个问题。这里先放上官网文档&#xff0c;不理解的地方…