JavaScript-前端加密(广义),防止js篡改
来源:互联网 发布:plc能用c语言编程吗 编辑:程序博客网 时间:2024/06/06 08:52
好久没有写博客了,不是没有在学习新东西,是最近比较忙,没有总结。
今天聊的话题是防止js篡改。javascript代码和需要编译的代码有一点不同是,网页上会直接请求js文件源码,而编译的语言运行时使用的是编译后的文件。即使这样,很多编译语言(比如java)都会做源码混淆(obfuscation),前端这样的纯文本应用,更需要做点什么保护我们的逻辑代码不被篡改和窥探。
防止js篡改的原理也很简单,就是增加破解的代价。一下源码可以在我的github查看。
注意:我这里说的前端加密,是对浏览器端的js文件加密,不是传输过程中的加密,不涉及hash摘要等
1. 降低可读性
1.1 压缩(compression)
很好理解,就是去掉注释、多于的空格、简化标识符等等。工具很多,YUI Compressor、UglifyJS、Google Closure Compiler等等。
1.2 混淆(obfuscation)
保证不破坏代码执行结果的情况下,让代码变得难以阅读。常用混淆规则:拆分字符串、拆分数组、增加废代码、,压缩其实也有一定混淆功能。本质就是改变输入代码字符串的抽象语法树(AST)的结构。其他工具:v8就是一个,还有mozilla的SpiderMonkey, 知名的esprima,还有uglify;商业混淆服务有:jscramble。
1.3 加密(encryption)
这里的加密指文本可逆编码,是狭义的加密,也就是我们常说的加密啦。这个部分依然是借助一些工具,如: Packer 、bcrypt等等。
2. 代码不放置在JS文件中
将代码放在非js文件中,增加定位难度。这里常用的方式有两种:放置到png中,通过HTML Canvas 2D Context获取二进制数据的特性,可以用图片来存储脚本资源;放置到css文件中,利用content样式可以存放字符串的特性,同样可以。
2.1 png
用png保存js代码,首先需要对png进行编码,然后使用的时候进行解码。借助canvas及base64和二进制编码。
编码
1、字符串转换成ascii码;
2、创建足够存储空间的canvas;
3、将字符填入到像素中(忽略alpha值);
4、获取data url;
canvas.toDataURL(“image/png”);
5、存为png图片。
function encodeUTF8(str) { return String(str).replace( /[\u0080-\u07ff]/g, function(c) { let cc = c.charCodeAt(0); return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f); } ).replace( /[\u0800-\uffff]/g, function(c) { let cc = c.charCodeAt(0); return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3f, 0x80 | cc & 0x3f); } );}function request(url, loaded) { let xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) if (xmlhttp.status == 200) loaded(xmlhttp); } xmlhttp.open("GET", url, true); xmlhttp.send();}void function(){ let source = '../image/test.js'; request(source, function(xmlhttp){ let text = encodeUTF8(xmlhttp.responseText); let pixel = Math.ceil((text.length + 2) / 3); // 1一个像素存3个字节, let size = Math.ceil(Math.sqrt(pixel)); //console.log([text.length, pixel, size, size * size * 3]); let canvas = document.createElement('canvas'); canvas.width = canvas.height = size; let context = canvas.getContext("2d"), imageData = context.getImageData(0, 0, canvas.width, canvas.height), pixels = imageData.data; for(let i = 0, j = 0, l = pixels.length; i < l; i++){ if (i % 4 == 3) { // alpha会影响png还原 pixels[i] = 255; continue; } let code = text.charCodeAt(j++); if (isNaN(code)) break; pixels[i] = code; } context.putImageData(imageData, 0, 0); document.getElementById('base64').src = canvas.toDataURL("image/png"); });}();
编码后的图片:
解码
1、加载png;
2、将png原尺寸绘制到canvas中;
3、读取像素中的字符串;
4、生成相应协议的data url使用。
void function(){ let source = '../image/test.png'; let img = document.createElement('img'); img.onload = function(){ let canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; let context = canvas.getContext("2d"); context.drawImage(img, 0, 0); let imageData = context.getImageData(0, 0, canvas.width, canvas.height), pixels = imageData.data; let script = document.createElement('script'); let buffer = []; for (let i = 0, l = pixels.length; i < l; i++) { if (i % 4 == 3) continue; // alpha会影响png还原 if (!pixels[i]) break; buffer.push(String.fromCharCode(pixels[i])); } script.src = 'data:text/javascript;charset=utf-8,' + encodeURIComponent(buffer.join('')); document.body.appendChild(script); script.onload = function(){ console.log('script is loaded!'); } img = null; } img.src = source;}();
这里需要手动下载编码后的图片,我没有写自动下载的函数,这又是另一个可以深入探讨的问题了,所以不过多扩展。
2.2 css
使用content就简单多啦。
let div = document.getElementById('content');let content = window.getComputedStyle(div, ':before').content;
只需要和上面代码一样,新建一个srcript标签,利用data协议,就可以执行content内保存的js代码啦。
3. 防止代码执行被截获
- 截获 eval() / new Function() 的示例代码
eval = function() { console.log('eval', JSON.stringify(arguments));};eval('console.log("Hello world!")');Function = function() { console.log('Function', JSON.stringify(arguments)); return function() {};};new Function('console.log("Hello world!")')();
但是可能不是全局使用:
(function(){}).constructor('console.log("Hello world!")')()
- 截获 constructor 的示例代码
Function.prototype.__defineGetter__('constructor', function () { return function () { console.log('constructor', JSON.stringify(arguments)); };});(function() {}).constructor('console.log("Hello world!")');
目前能想到的是判断 eval 是否被重定向
示例,如果 eval 被重定向 z 变量不会被泄露
(function(x){ var z = 'console.log("Hello world!")'; eval('function x(){eval(z)}'); x();})(function() { /* ... */ });
其实,还应该阻止使用者打开控制台,进行debug。参考stackoverflow中的讨论,很好用。
本来还想在这篇博客里总结下js的执行方式,限于篇幅,下次再说吧。
- JavaScript-前端加密(广义),防止js篡改
- 前端js加密方式
- crypto-js HMACSHA256加密 前端js加密
- 【前端Js】高级加密解密标准AES加密(Javascript代码实现)
- 如何防止Android apk 恶意篡改,爱加密APK安全加固
- IDEA加密文件Base64转换String传输以及报文摘要MD5防止恶意篡改
- Android数据传输增加签名认证(防止数据被篡改)
- js前端des加密插件
- MD5.js,前端MD5加密
- 前端jq-MD5加密 + js-base64加密
- js javascript 加密
- JAVASCRIPT加密方法,JS加密解密综述(7种)
- JAVASCRIPT加密方法,JS加密解密综述(7种)
- JAVASCRIPT加密方法,JS加密解密综述(7种)
- Java加密-SHA算法家族(前端js,后台java)
- JS本地加密防止嗅探
- 前端页面js防止重复提交表单
- 前端输入框如何防止js攻击
- scanf
- 第四周《C语言及程序设计》实践项目5 认识递归
- freeMarker学习(一)
- linux并发同步
- 0001-每日一记(2016-09-15)
- JavaScript-前端加密(广义),防止js篡改
- 第三周项目3-求集合并集
- Qt学习之十六:Qt绘制系统简介
- Selenium学习10--Frame操作
- DB2 del文件导出指定分隔符问题
- 逆天,自我意识
- eclipse中建立servlet时会提示没有导入javax.servlet包解决办法
- Ubuntu下boot分区剩余空间不足解决
- 既爱又恨的inline-block