前端的安全性一直是我们在考虑安全问题时,没有办法绕过的关键问题。今天,我就来和你聊一聊如何保护前端的安全性。

我们先来看一个攻击事件。2017 年,12306 网站被曝出有“买下铺”的功能。我们都有过买票的经历,当我们在 12306 上买卧铺的时候,是没法选择上铺、中铺还是下铺的。但是,有人去分析了 12306 的前端代码,发现里面其实包含了选铺位的功能,只是默认为随机,没有展示出来。所以,有人通过篡改前端代码,就将这个功能开放出来了。

一旦黑客能够完全摸清楚应用的前端代码,就能够任意地篡改前端的逻辑,实现带有想要功能的前端应用了。

如果说 12306 的例子,还不足以让你对前端安全产生警惕的话,你可以想一想,我们在网上看到的各种所谓的“破解版”软件,其实都是人为修改了应用的前端认证功能,从而不需要认证就可以正常使用。

除了篡改前端代码,黑客还可以通过对前后端接口的调用过程进行分析,复刻出一个自己的前端应用。在黑客复刻的前端应用中,所有的接口认证和加密都合法,只是调用的顺序完全由黑客掌控。粉丝圈比较流行的各类明星应援工具,其实都是基于这个原理实现的:黑客通过分析微博客户端的接口,自己打包了一个前端应用,实现了一键关注、点赞等功能。因为这些接口都是合法的,所以后端人员很难分辨出这些请求是来自于正规的应用,还是黑客自己实现的应用。

针对前端的攻击可以说是“防不胜防”,这让后端没有办法信任前端的环境,甚至没有办法信任前端发起的请求和上传的数据,极大地影响了公司和应用的正常发展。那么,我们应该通过什么方法来保障前端的可信呢?

▌什么是混淆技术?

要解决这个问题,我们可以先想一下黑客攻击前端的过程:黑客通过分析前端代码,来篡改前端逻辑,实现带有想要功能的前端应用。那有没有一种方法,无法让黑客在前端代码中分析出有效信息呢?答案就是混淆。

在理想状态下,我们混淆了前端代码之后,不仅能让黑客无法篡改前端代码,还能保证即使黑客成功篡改代码,那么篡改后的前端代码依然不可用。同时,黑客无法获得前端的接口密钥和签名等信息,也就无法伪造正常的前端应用去发起请求了。

我们知道,安全中通常不存在理想状态。我们最需要做的,就是不断地升级对抗,来接近这个理想的目标。

刚才我们说的是混淆技术可以实现的结果,那混淆技术究竟是什么呢?在不同的语言和环境(如:Android 、iOS 和 Web)中,混淆技术都是相对独立的。尽管混淆技术相对独立,但我还是希望,你可以通过理解一门语言的混淆技术和思路,做到“一通百通”。我也希望能够更好地启发你去思考,如何去做好前端安全。接下来,我就以 JavaScript 为例,带你梳理混淆的常见技术和思路。

▌1. 清晰代码无序化

在实际工作中,开发人员总是会要求自己写出清晰简洁的代码。但是,这也为黑客的代码分析提供了便利。因此,混淆的第一步,一定是想办法让我们的 JavaScript 代码变得“难看”,也就是将整洁的代码变得无序。

有什么办法能让代码变得“难看”呢?我这里通过一个例子来具体解释一下,你就能明白了。

我们先来看一段代码。

function obfuscate() {console.log("I'm obfuscator!");
}
obfuscate();

我们一眼就能够看出这段代码的逻辑:有一个 obfucate 方法,这个方法会打出一行日志,日志内容为“I’m obfuscator!”。

在 JavaScript 中,空格、换行、缩进这些内容,只是为了让代码看起来更清晰。所以,这些对代码没有影响,只是便于开发人员查看的内容,完全可以去除。这样一来,这段代码我们就可以改成下面这样:

function obfuscate(){console['log']('I'm obfuscator!');}obfuscate();

把代码压缩成一行后,黑客想要阅读就已经比较吃力。在此基础上,我们还可以让它变得更“难看”。实际上,JavaScript 中的方法名和变量名也不影响逻辑执行,只是开发人员用来表示方法和变量的含义,完全可以用没有意义的随机字符替代。随机字符代替后的效果如下:

function _0xc648a(){console['log']('I\x27m\x20obfuscator!');}_0xc648a();
▌2. 简单逻辑复杂化

对于上面这段无序化后的代码,只要黑客稍微花点心思去阅读,再配合一些 JavaScript 格式化的工具,也能够弄明白它的逻辑。归根结底还是因为这段代码“太简单了”。那么,我们是不是能够让原本简单的代码变得复杂呢?实现方法有很多种,我们先来看最简单的一种:加入无意义的代码。

我们还是以最开始的简单代码为例。为了方便你查看,我把前面那段简单代码重新贴在这里。

function obfuscate() {console.log("I'm obfuscator!");
}
obfuscate();

在这段代码中,本来输出的日志就是一个固定的字符串“I’m obfuscator!”。但是,我们可以先将这段字符串放在一个字典中,然后再通过字典去获取字符串。修改后的效果如下:

function obfuscate() {var _0x16df9a = { 'HXGCi': 'I\x27m\x20obfuscator!' };
console['log'](_0x16df9a['HXGCi']);
}
obfuscate();

这就是通过字典等形式,将常量变成变量的混淆方法。在此基础上,我们还可以加入一些无意义的 switch、if 和 while 语句,进一步将代码复杂化。

除了加入一些无意义的代码,我们还可以加入一些不会被执行的代码,让混淆的结果更有威慑力。比如下面这段代码:

(function (_0x2177d9, _0x1442cc) {var _0xb84613 = function (_0x5a2b5f) {while (--_0x5a2b5f) {_0x2177d9['push'](_0x2177d9['shift']());
}
};
_0xb84613(++_0x1442cc);
}(_0x1808, 0x1ea));
function obfuscate() {console['log']('I\x27m\x20obfuscator!');
}
obfuscate();

在这段代码中,中间的 function (_0x2177d9, _0x1442cc) 就不会被执行,它的目的仅仅是让代码看起来更复杂而已。

▌3. 固定字符动态化

在我们前面说的这几个混淆代码的例子中,关键字符串“I’m obfuscator!”始终都存在。如果黑客关心的只是这个字符串,那它通过搜索就可以很快定位到。也就是说,通过前面几种方式混淆的前端代码,其中的接口、密钥和签名等信息,黑客还是很容易就可以获取到。

既然关键字符串“存在”于代码中就不安全,那有没有方法可以让关键字符串“消失”呢?我们可以通过加入一些逻辑,让这些关键字符串只有在实际运行的时候,才会被计算出来。

最简单、最直接的思路就是,我们可以将关键字符串改成多个字符串拼接的形式。效果如下:

function obfuscate() {console['log']('I\x27m\x20o' + 'bfusc' + 'ator!');
}
obfuscate();

通过这样改写的方式,黑客就没有办法通过搜索功能,找到“I’m obfuscator!”的位置了。

但是,这种简单分割字符串的方式很容易被发现。所以,我们可以将这些字符串从它原本的位置拿出来,通过更复杂的方法(如:数组的引用、方法的调用等)来获取。效果如下:

var _0x5e96 = [
'bfusc',
'ator!',
'log',
'I\x27m\x20o'
];
(function (_0x520fe6, _0x366376) {var _0x38fe5f = function (_0x456d44) {while (--_0x456d44) {_0x520fe6['push'](_0x520fe6['shift']());
}
};
_0x38fe5f(++_0x366376);
}(_0x5e96, 0x15e));
var _0x40ca = function (_0x520fe6, _0x366376) {_0x520fe6 = _0x520fe6 - 0x0;
var _0x38fe5f = _0x5e96[_0x520fe6];
return _0x38fe5f;
};
function obfuscate() {console[_0x40ca('0x0')](_0x40ca('0x1') + _0x40ca('0x2') + _0x40ca('0x3'));
}
obfuscate();

这样一来,黑客想要快速找到 _0x40ca(‘0x1’) 具体指什么,就需要花上一番功夫了。

▌4. 反调试

前面 3 种技术都是直接对源码进行混淆。但是,大多数情况下,黑客在分析代码的时候,不是直接阅读源码,而是通过调试的方法在 JavaScript 代码运行过程中,获取实际的代码执行方向以及变量的值。因此,为了保护前端安全,我们要采用反调试技术。在 JavaScript 中,主要有两种方法可以对抗调试:域名锁定和无限断点。下面,我们一一来看。

第一种是域名锁定。

当黑客来想要分析一个网页的时候,通常会将代码下载下来放到本地运行。但是,我们更希望这个分析过程仍然发生在当前的域名下,这样我们就能够通过请求去分析黑客到底干了什么。因此,我们可以在 JavaScript 中加入一段域名判断的逻辑。这样一来,当 JavaScript 运行的环境是 localhost(本地主机)域名,或者其他未知的域名时,JavaScript 就会产生错误,黑客就无法正常运行下载后的 JavaScript 文件了。

我来举个例子。在 JavaScript 中,我们可以通过 window.location.host 获取当前域名,然后判断这个域名是否等于网站的域名,比如 server.com。如果不等于的话, 说明 JavaScript 不是通过正常访问域名的形式执行的。因此,JavaScript 会直接返回,不执行后续的逻辑。代码如下:

function obfuscate() {if(window.location.host != 'server.com'){return;
}
console.log("I'm obfuscator!");
}
obfuscate();

第二种是无线断点。

在调式技术中,我们最常用到的功能就是断点。通过设置断点,我们可以让程序停留在某一个代码或者指令上,方便查看停留的这个时刻中各个变量的具体值是什么。

在 JavaScript 中,debugger 指令就是用来添加断点的。所以,在反调试的时候,我们可以在 JavaScript 中开启一个单独的线程,来循环调用 debugger。这样一来,如果黑客进入到调试模式,就会不断地停滞在无意义的断点处,从而无法正常调试。在正常运行 JavaScript 的时候,debugger 不会生效,也就不会影响用户的正常使用。

除此之外,针对提供了额外的 JavaScript 接口的浏览器(比如 Chrome),我们可以通过在 JavaScript 中检测开发者工具是否开启等特征,来实现反调试。开发者工具是开发人员在调试过程中必须使用的工具,一旦开启,基本就代表已经进入调试状态了。因此,我们可以在检测到开发者工具开启的时候,不去执行正常的 JavaScript 逻辑,这样就能够起到反调试的作用了。

好了,说完了这 4 种混淆技术,我要提醒你一点。这些混淆技术不是独立使用的,而应该是组合使用的。完整的混淆流程应该是这样的:首先,我们可以在原有的 JavaScript 代码中加入反调试的逻辑,然后通过简单逻辑复杂化和固定字符动态化的方法,隐藏原有的逻辑和反调试的逻辑。最后,通过清晰代码无序化,将所有的额外信息进行剔除,最终将代码变成了压缩成一行的 JavaScript 文件。

混淆技术有什么负面影响?

尽管混淆技术是保护前端安全的重要技术,但混淆技术改变了前端代码,就不可避免会影响前端的功能。这也是混淆始终达不到理想状态的一个主要原因。对于 JavaScript 的混淆来说,它的负面影响主要包括三个方面:增加体积、影响性能和无法分析报错。

混淆带来的最直接影响就是增加代码体积。在固定字符动态化的例子中,原本简单的 4 行代码经过混淆之后,变成了几十行。如果应用更复杂一些,一个几 KB 的 JavaScript 文件经过混淆之后变成几百 KB,也是很正常的事情。这样一来,用户网络加载一个大型的 JavaScript 文件,所面对的消耗、加载时的延迟以及运行时的内存等都会有明显增长。

除了增加代码体积以外,混淆还会增加额外的执行逻辑,降低代码执行的速度影响性能。比如说,console.log 本来只是一个简单的指令,但是在混淆之后,JavaScript 需要对它进行数据的取值、索引的计算以及字符串的拼接等操作。这样一来,混淆后的代码执行速度必然会下降。

而且这些无用的操作,事实上是可以无限添加的。因此,在混淆的时候,如何把控复杂化的程度,是我们需要谨慎考量和测试的。

还有一点是不可避免的,那就是混淆后的代码,不仅黑客无法阅读,你其实也无法阅读。在混淆之前,如果前端出现错误,我们可以直接通过错误信息定位错误;但是在混淆之后,错误信息会变得“很难看”,而且代码只会剩下一行,我们也就无法定位了。

你还需要注意一点:混淆不可能让代码变得完全不可读。因为你的代码最终需要执行在用户终端,而执行的条件就是终端能够读懂代码。以 JavaScript 为例,黑客完全可以自己定义一个浏览器来执行 JavaScript 代码。这样一来,尽管黑客没办法直接阅读 JavaScript 文件,但仍然可以通过浏览器执行的指令集和内存环境来进行分析。

▌总结

好了,今天的加餐就到这里。

我们主要以 JavaScript 为例,梳理了混淆的主要技术和思路。虽然通过混淆,我们能大大增加黑客分析前端代码的难度,但是,混淆同样会给我们的正常工作和应用的执行增加难度,带来负面影响。所以,我们在使用混淆技术的时候,必须要经过谨慎的考量和测试。

▌思考题

最后,还是给你留一道思考题。

我们知道,不同的语言和环境,其混淆的技术和思路都存在各自的特点。你可以试着分析一下,在你熟悉的语言和环境中,有哪些方式可以用来进行代码混淆?

欢迎留言和我分享你的思考和疑惑,也欢迎你把文章分享给你的朋友。我们下一讲再见!

▌下一讲

职业发展:应聘安全工程师,我需要注意什么?

35 | 前端安全:如何打造一个可信的前端环境?相关推荐

  1. 使用开源微前端框架 Luigi 创建一个基于微前端架构的工程

    官网地址 微前端通常被称为"前端微服务". 它们允许您将大型单体前端分解为独立的.可扩展的.可以协同工作的独立部分. 微前端架构对于复杂的产品或拥有众多团队的公司尤其有用,可以帮助 ...

  2. 在Windows环境下用Editplus打造一个Python编辑调试环境

    Python有很多集成开发工具,商业的有 komodo, Wingide, Boa等,还有 Python自带的集成环境 IDLE, windows下还有 Pythonwin等.本文 利用一个很流行的功 ...

  3. bootstrap设计登录页面_前端小白如何在10分钟内打造一个爆款Web响应式登录界面?...

    对于前端小白(例如:专注后端代码N年的攻城狮),自己编写一个漂亮的Web登录页面似乎在设计上有些捉襟见肘,不懂UI设计,颜色搭配极度的混乱(主色,辅助色,配色,色彩渐变,动画效果等等,看起来一堆乱七八 ...

  4. react在线文件_【前端新手也能做大项目】:从零打造一个属于自己的在线Visio项目实战【ReactJS 】 (一)...

    本系列教程是教大家如何根据开源js绘图库,打造一个属于自己的在线绘图软件.当然,也可以看着是这个绘图库的开发教程.如果你觉得好,欢迎点个赞,让我们更有动力去做好! 本系列教程重点介绍如何开发自己的绘图 ...

  5. 如何打造一个经常宕机的业务系统?

    作者| Mr.K   整理| Emma 来源| 技术领导力(ID:jishulingdaoli) 删库跑路专家.宕机行为艺术家.肥胖版吴彦祖--老K,曾经说过:"打造一个偶尔宕机的系统并不难 ...

  6. vue.js+socket.io打造一个好玩的新闻社区

    title: Socket.io+vue打造新闻社区 date: 2017-06-12 20:19:05 tags: [vue.js,javascript,socket.io] vue2.0 + so ...

  7. mysql 分词搜索_打造一个蓝奏云网盘搜索引擎

    目前存在的网盘搜索引擎好多,可以归于两类: 1.搜索引擎索引 2.爬虫入库索引 第一种:搜索引擎索引 这种索引方式是目前比较主流的方式,依靠百度,谷歌建立索引链接,当用户进行搜索的时候,网盘搜索引擎会 ...

  8. 学习 sentry 源码整体架构,打造属于自己的前端异常监控SDK

    前言 这是学习源码整体架构第四篇.整体架构这词语好像有点大,姑且就算是源码整体结构吧,主要就是学习是代码整体结构,不深究其他不是主线的具体函数的实现.文章学习的是打包整合后的代码,不是实际仓库中的拆分 ...

  9. 使用jquery打造一个动态的预览产品颜色效果

    在浏览一些电子商务网站的时候,选择一件产品的时候,我们经常会看到点击衣服的颜色,同一件衣服的颜色就会切换,让我们觉得真是比较有意思,这样做的效果给用户的体验比较好,今天就给大家分享一下这种效果的实现原 ...

最新文章

  1. Xilinx基于模型的设计工具—Model Composer
  2. mysql 取年月日 语句_MySQL学习从这里出发!
  3. Windows2000、XP、2003系统万能Ghost全攻略
  4. mysql命令行导入dmp文件,oracle的备份文件(dmp)导入mysql数据库方法
  5. nvidia控制面板点了没反应win7_win7 nvidia控制面板没反应打不开 解决办法
  6. flutter视频播放videoplayer与chewie
  7. mysql服务器版本手册_MySQL中文参考手册——与MySQL服务器连接
  8. 验证集与测试集的区别
  9. Qt下绘制PPI扫描的雷达图
  10. 笔记本触摸屏使用技巧
  11. vue中使用svg图片
  12. Python函数命名-PEP8编码规范的说明及IDE提示的忽略
  13. 经验分享:《节奏大师》UI优化历程
  14. 日记侠:朋友圈未死,从今天起,你要开始赋能朋友圈
  15. 《计算机图形学》实验报告区域填充扫描线算法
  16. 大一转专业计算机考什么,武汉大学 计算机 转专业 经验贴
  17. 三维空间到四维空间的距离遐想
  18. 十所大学!计算机第二学士学位开始报名!
  19. CSS 3D转换和动画
  20. 学习笔记(01):Qt视频教程-基础实例进阶-嵌入windows桌面图标下的窗口程序

热门文章

  1. Spring Boot 中使用 Hikari连接各类数据源
  2. 费马小定理看了等于没看证明
  3. 数据质量问题是大数据应用的关键
  4. matlab 矩阵 列 逆序,matlab对矩阵/向量的常用操作(拼接矩阵、向量逆序、改变矩阵形状、求行阶梯形矩阵、提取矩阵的一部分等)...
  5. jQuery 删除HTML元素
  6. matlab的一点内容
  7. 微信公众号支付,iframe跨域
  8. Linux下 PyDev + Eclipse安装方法
  9. Go语言学习之打印九九乘法表
  10. c++ 贪心法构造货币统计问题