以变制变——前端动态化代码保护方案探索

欢迎你们前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~css

本文分享了腾讯防水墙团队关于机器对抗的动态化思路,但愿能抛砖引玉,给如今正在作人机对抗的团队一些启发,帮助更多中小型公司的业务摆脱机器和爬虫之痛。html

0x00 前言

浏览器做为当今互联网的一大流量入口,正在变得愈来愈强大。为了有更好的Web体验,各种新的标准被制定并实施。PWA的出现,更是把移动端H5的体验推向了另外一个极致。愈来愈多业务使用H5做为主要入口的同时,也带来了另外一个问题:机器行为泛滥。只要有利益的地方就会有恶意,登陆注册、投票领券等页面很容易成为机器刷量的重灾区,现在写一个普通刷投票脚本的难度基本就跟写一个“Hello World!”的难度差很少。在与机器对抗的历程中,Web前端一直是很是薄弱的一环。浏览器毫无保留地把全部前端代码拉取到本地并执行、全部前端代码均透明可见,拿什么拯救前端代码安全?前端

0x01 名词解释

代码安全

本文中所说起的代码安全,是指前端JavaScript代码的安全。一般,若是一段JavaScript代码只能在正常的浏览器中运行,没法或还没有在非正常浏览器的运行环境执行获得结果、没法被等价翻译成其余编程语言的代码,则认为这段代码是安全的。node

一段重要的JavaScript逻辑被置于其余环境以高于正常浏览器几个数量级的效率运行并获得正确的结果,对于服务端及后面的业务来讲,几乎是一个灾难。webpack

数据保护

本文说的数据保护是指对HTTP/HTTPS协议上承载内容(如POST的body)的保护。HTTP协议是一个文本的协议,全部传输的内容从客户端(即浏览器)的角度看都是可见且富有语义的,这意味着内容若是不加以保护,恶意用户只须要理解内容中的各项参数,便可模拟相应的请求而无需阅读或逆向前端JavaScript代码的逻辑。注意,这里说的保护不是指TLS等传输过程的保护,而是指HTTP协议上层承载的具体数据内容的保护。git

好比,正常一个查询请求的URL形如https://example.com/query?from=shenzhen&destination=beijing,爬虫开发者无需阅读JavaScript即可知道参数要如何构造。而若是请求形如https://example.com/query?params=ZnJvbT1zaGVuemhlbiZkZXN0aW5hdGlvbj1iZWlqaW5n,恶意用户在没法经过观察当即知道参数构造方法的前提下,只能阅读或须要先逆向JavaScript代码,才能知道构造参数方法。这样就达到了对数据进行保护的目的。github

混淆

经过一些字符串替换规则或者抽象语法树变换规则,将一段代码等价替换成另外一段可读性不好的代码,从而达到保护原有代码安全。这个过程一般是不可逆的。web

如:面试

function foo() {    console.log('hello world!');
}
foo();
复制代码

被变换成:正则表达式

var a = 'console', b = 'log', c = 'hello', d = ' world!';function e() {  window[a][b](c + d);
}
e();
复制代码

此时代码的可读性被下降了,这是一种很简单的混淆方式。

一些能被搜索引擎搜索到的文章会将代码压缩与混淆混为一谈,相似Uglify的工具能把代码压缩成可读性很低的代码,以下图:

img

但被浏览器强大的格式化功能格式化以后,各类逻辑仍然尽收眼底。

img

代码压缩工具并不会对代码起到太多的保护做用,其做用只是缩短变量名、删减空格以及删除未被使用的代码,这些工具的目的是优化而非保护,只能“防君子而不防小人”。为了进一步保护前端代码,须要使用一些代码混淆工具。

0x02 常规化方案及缺陷

1. 可逆变换保护数据

常规的数据保护方式是设计一个可逆变换函数f对数据进行变换,浏览器端提交给服务端的数据 d 通过该可逆变换函数 f 处理后获得变换后的数据 d′

d′=f(d)

d′ 提交到服务端后使用反函数 f−1 便可获得原数据d。

d=f−1(d)

若是恶意用户不知道f的运算步骤,则没法构造出合法的d′。其中 f 能够是一种数据处理算法,也能够是一种加密算法。可是,因为 f 的运算步骤是固定的,且算法最终执行在浏览器中,即便 f 是一种私有的数据处理算法,也终究会被逆向获得其运算步骤。现在nodejs已经至关成熟,在未使用任何混淆工具对 f 进行保护的前提下,恶意用户可直接从前端JavaScript代码中截取出核心逻辑,不须要太多成本即可编写出能在nodejs上运行的破解工具。

特别的,若是函数 f 中含有额外的参数 p ,运算步骤形如 d′=f(d,p) ,此时变换 p 并不能增长 f 的安全性。恶意用户在截取变换函数f的同时能够一并获得参数p,而且在未经混淆的代码里,p是很容易使用工具提取的。

所以,只有数据变换是很难很好保护数据的。那么再对代码进行混淆是否能够有效保护数据呢?

2. 对代码进行混淆保护

目前能找到的公开混淆工具并很少,常见的有:

  • jscrambler(商业)
  • JavaScript-Obfuscator(开源)

jscrambler做为一款商业软件,混淆效果好,但其付费计划较为昂贵。JavaScript-Obfuscator是目前比较热门的一款开源混淆器,但混淆效果不尽人意。为了验证JavaScript-Obfuscator混淆效果,本文以字符串混淆为例,编写了一个简单的脚本对通过JavaScript-Obfuscator混淆后的字符串进行自动化还原,代码开源请戳:https://github.com/conanliu/de-js-obfuscator

有了这个工具,逆向出字符串的成本几乎为0。其实逆向字符串相对比较简单,但这已是个开始,逆向出逻辑是早晚的事。普通强度的混淆能够在一段时间内保护业务逻辑,一段时间之后,代码便没那么安全了。以JavaScript-Obfuscator的混淆强度,「一段时间」一般不会超过一周。若是页面承载的是一个高收益多恶意的业务,即便页面的JavaScript代码被JavaScript-Obfuscator混淆过,上线一周时间后,大部分关键逻辑也可能已经被逆向出来了。关键逻辑被逆向意味着刷量工具很快会被编写出来,该业务将面临被刷的风险。

对于一个正常的业务来讲,JavaScript中数据保护相关的逻辑一个月变化一次已经至关频繁了。若是为了达到较好的抗破解须要在一周改变一次逻辑,这种对抗成本是很高的。那么有没有一种长效的机制,既能保证前端代码的安全,而又不须要付出过量的成本呢?本文后面试图从动态化的角度,探索一种新的人机对抗方式。

0x03 动态化方案介绍

若是咱们有5个数据变换函数 f1,f2,f3,f4,f5,针对每次请求,咱们随机挑选2个变换函数 fx 和 fy,并随机挑选一个分隔符 s ,真实数据 d 被随机拆分红 d1和 d2 ,最终数据为

d′=combine(fx(d1),s,fy(d2))

d′提交到服务端后,服务端进行切分操做获得一个二元组

(d′1,d′2)=split(d′,s)

再用fx和fy的反函数处理d′1和d′2,最终获得原始数据:

d1=f−1x(d′1)

d2=f−1y(d′2)

d=d1+d2

虽然单次破解的难度仍为t≈1week,但因为每次请求对应的算法组合均不一样,单次破解后并不适用与后面的请求。所以理论上须要逆向并脚本化该逻辑的时间代价是指数级增加的,最终恶意用户由于逆向成本过高,而转向了使用起来更简单的模拟器。至于模拟器的对抗不在本文的讨论中。但能够明确的是,模拟器的对抗比自动脚本的对抗要容易一些。同时因为执行模拟器比执行自动脚本须要更多的资源,这也无形中增长的恶意的做恶成本,最终致使恶意在投入和产出中失衡。

该动态化方案虽然听起来可行,但在实际工程化中会遇到不少问题:

  1. 如何标识某次请求的函数组合?
  2. 如何权衡页面性能?
  3. 如何解决js编译速度太慢的问题?
  4. 是否须要混淆?

接下来将针对以上问题,探索如何在工程上一一解决。

0x04 工程化问题探索

1. 如何标识某次请求的函数组合?

通过随机组合后用户每次获得的js都可能不一样,此时须要有一个标识告知服务端 fx 和 fy 分别是哪两个。一种可行的方案是将 x 和 y 的内容连同变换函数 fx 和 fy一块儿,直接明文编码到js中,提交数据时将x和y跟随d′一块儿提交。但这种标识容易被某些正则规则直接从js文件中提取,恶意用户可遍历出全部变换函数及其对应的逻辑,再根据匹配出的标识进行组合。

更严谨的作法是编译js文件时生成一个签名串signature,将该signature做为变量编译到js文件中。最后signature连同生成的数据d′一块儿提交到服务端,服务端使用f−1sig(signature)获得解密d′的关键参数,进而对d′进行解密。签名的生成算法可表示成以下形式:

signature=fsig(x,y,s,random,timestamp)

其中x为第一个变换函数的标识,y为第二个变换函数的标识,s为分隔符,random为一个随机数,timestamp为生成签名的时间戳。设计随机数的目的是让每次生成的签名均不一样,而时间戳能够感知签名对应js文件的新鲜度,而且必定程度上能对重放攻击进行汇集。

2. 如何权衡页面性能?

前端页面性能是一个Web应用必然会关注的问题,一种通用而有效的性能优化方式是合理地为页面中的资源文件设置缓存。一般对于一个模块化良好且使用成熟打包工具打包的项目,入口html的缓存策略会被配置为Cache-Control: no-cache,而js/css/image等资源文件会设置一个比较长的缓存时间。但负责数据保护的js文件若是含有动态生成的逻辑,该js文件将不能再使用缓存,不然一旦缓存时间控制不当,将会引起各种数据解密失败的问题。

正常状况下在人机对抗的场景中,页面并不须要对全部的请求均作人机验证,也就是说,负责人机验证的JavaScript代码并不会被正经常使用户访问屡次,因此在人机验证的环节,部分基于缓存的优化是能够省略的。理想状况下,用户在一段时间内仅会访问一次人机验证的逻辑。此时要作好的是保证用户首次加载的体验,而二次访问的体验能够暂且不予考虑。

建议的方案是将数据保护相关的逻辑从整个工程的JavaScript代码中剥离出来,直接inline编译到html页面中,或者编译到一个独立的js文件中,为该js文件单独设置Cache-Control: no-cache的response头部。该js与其余js之间可使用全局变量、postMessage等方式通讯。

3. 如何解决js编译速度太慢的问题?

前端的打包工具备不少,如gulp、webpack、Rollup等,这些工具各有长处,也有不少针对编译过程的优化,但目前都没法在须要毫秒级响应的场景完成一次打包,所以编译打包须要异步完成。那么如何生成出足够数量的js知足正常访问和对抗场景的需求?比较简单的方案是循环跑编译脚本,编译好一个替换一次,短期内用户可能会访问到同一个js,随着旧js被新编译出来的js替换,一段时间内用户访问的js能够认为是随机的,此时js的变换间隔取决于编译速度。

除了简单方案外,这里介绍一种更灵活的方案,即将编译产物缓存并提供随机访问。首先,把安全相关的js文件从静态服务中剥离出来,由一个后端的web server输出js内容。该server上维护着一个长度必定的数组,构建工具编译好一个js文件后,将该文件的内容发送给web server,web server将接收到的内容顺序填充到数组中;当有用户页面时,浏览器向web server请求该js内容,web server从数组中随机挑选一个,返回给浏览器。

这种服务方式有不少好处,除了能够保证安全js的随机性,还能将signature的生成放到web server中完成。构建工具在编译js时将编译的元信息发送给web server,此时并不生成出signature。用户须要请求该js时再根据元信息实时生成一个signature,填充到js文件内容中。这样生成的signature每次都是独立的,经过检测signature的使用次数,能够很容易标识并拦截重放的请求。

img

4. 是否须要混淆?

既然有了随机的动态,是否还须要混淆?答案是确定的。虽然fx和fy每次都不同,但两个不一样的变化函数,必定会有其独有的特征。例如fx和fy在JavaScript中的算法实现以下:

function foo(x) {
  x = String.fromCharCode.apply(null, x.split('').map(i => i.charCodeAt(0) + 23);  return btoa(x)
}function bar(y) {
  y = String.fromCharCode.apply(null, y.split('').reverse().map(i => i.charCodeAt(0) + 13);  return btoa(y);
}
复制代码

本例中2三、reverse、13即是fx和fy的特征,若是某次请求的js文件中包含reverse和13,则很大多是使用了bar变换,若是包含23,则很大可能使用了foo变换。经过这种特征检测,能够轻松获得请求的js中使用了何种变换组合。而检测方法并不会很复杂,只须要一些简单正则表达式便可。

0x05 总结

本文分析了常规数据保护及混淆的短板,并从动态的角度,给出了一种对抗机器行为的方式,同时在工程化上有一些思考。人机对抗之路艰辛且漫长,是在将来很长时间内都会存在的业务安全问题。但愿动态化思路能给如今正在作人机对抗的团队一些启发,帮助更多中小型公司的业务摆脱机器和爬虫之痛。另外,腾讯防水墙团队在机器对抗上有较深的积累,若是想直接体验成果能够点击此处:https://007.qq.com


问答

腾讯云域名安全认证问题?

相关阅读

腾讯安全平台部专家研究员胡育辉:千亿黑产背后的破局之道

全景解析腾讯云安全:从八大领域输出全链路智慧安全能力

手机黑卡,这个仇我是记下了


此文已由做者受权腾讯云+社区发布,原文连接:https://cloud.tencent.com/developer/article/1145048?fromSource=waitui

欢迎你们前往腾讯云+社区或关注云加社区微信公众号(QcloudCommunity),第一时间获取更多海量技术实践干货哦~

相关文章
相关标签/搜索