사람들은 HTML 암호화를 난독화하는 방법에 대해 문의해 왔습니다. 실제로 이는 업계의 많은 사람들이 연구해 온 주제입니다.
연휴 동안 여러분과 공유하고 싶은 글을 정리했습니다.
먼저 요구 사항을 이해해 봅시다. 암호화의 목적은 무엇입니까? 암호화 수준은 어느 정도인가요? 이를 위해 우리는 무엇을 희생할 수 있습니까? 우리는 이 세상에 절대적인 보안은 없으며 암호화는 깨지고 난독화는 해독될 것임을 알고 있습니다. 기술 초보자, 개발자, 해커는 모두 완전히 다른 수준에 있으며, 수준에 따라 사람들을 예방하는 전략도 다릅니다. 예방 노력이 클수록 전문 보안 업체를 고용하는 등의 투자 비용도 커집니다. 투자 외에도 프로그램의 실행 성능과 사용자 경험도 고려해야 합니다. 암호화된 코드는 런타임 시 복호화되어야 합니다. 특히 HTML을 난독화한 후에는 프로그램의 실행 성능이 저하됩니다. 이러한 유형의 소스 코드 보호가 실제로 필요한지 여부는 신중한 결정이 필요합니다. 일반적으로 프론트엔드 코드는 사용자 경험을 담당하고, 백엔드 코드는 보다 안전한 데이터 처리를 담당합니다. 프런트 엔드에서 기밀 정보를 너무 많이 유출하지 마세요. 그러면 암호화가 특별히 의미가 없습니다.
고급 알고리즘 등 프런트 엔드 코드에서는 보호할 가치가 있는 콘텐츠를 거의 볼 수 없습니다. 많은 코드를 보호하기 위해 사용자 경험을 희생할 필요가 없습니다. 그러나 일부 프런트 엔드 코드에는 최종 사용자 데이터 보안이 포함되므로 현재로서는 데이터를 보호하기 위한 노력이 계속 이루어져야 합니다.
은 이해하기 쉽습니다. 즉, 주석 제거, 과도한 공백 제거, 식별자 단순화 등을 의미합니다. YUI Compressor, UglifyJS, Google Closure Compiler 등 다양한 도구가 있습니다.
은 코드의 실행 결과를 손상시키지 않으면서 코드를 읽기 어렵게 만듭니다. 일반적으로 사용되는 난독화 규칙: 문자열 분할, 배열 분할, 낭비 코드 추가, 압축에는 실제로 특정 난독화 기능이 있습니다. 핵심은 입력 코드 문자열의 추상 구문 트리(AST) 구조를 변경하는 것입니다. 기타 도구: v8이 그 중 하나이며 Mozilla의 SpiderMonkey, 잘 알려진 esprima 및 uglify 등의 상업용 난독화 서비스가 있습니다.
여기서 암호화는 텍스트 가역 인코딩을 의미하며, 좁은 의미의 암호화로 우리가 흔히 암호화라고 부르는 것입니다. 이 부분은 여전히 Packer, bcrypt 등과 같은 일부 도구에 의존합니다.
위치 지정이 더 어려워지도록 js가 아닌 파일에 코드를 넣습니다. 여기에는 일반적으로 사용되는 두 가지 방법이 있습니다. png에 배치하고, HTML Canvas 2D 컨텍스트를 통해 바이너리 데이터의 특성을 얻고, 이미지를 사용하여 스크립트 리소스를 저장하고, 콘텐츠 스타일을 사용하여 특성을 저장합니다. 문자열도 마찬가지입니다.
JS 코드를 png로 저장하려면 먼저 png를 인코딩한 후 사용할 때 디코딩해야 합니다. 캔버스와 base64, 바이너리 인코딩을 사용합니다.
2. 충분한 저장 공간을 갖춘 캔버스를 만듭니다.
3.
4. 데이터 URL을 가져옵니다.
canvas.toDataURL(“image/png”)
5.
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"); }); }();
Decoding
2. png 원본 크기를 캔버스에 그립니다. 픽셀 문자열
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
콘텐츠를 사용하기가 훨씬 쉽습니다.
let div = document.getElementById('content');let content = window.getComputedStyle(div, ':before').content;
위 코드와 같이 새로운 srcript 태그를 생성하고 데이터 프로토콜을 사용하여 콘텐츠에 저장된 js 코드를 실행하면 됩니다.
截获 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 变量不会被泄露
<span style="font-size: 18px"><code class="language-js hljs has-numbering">(<span class="hljs-function">function<span class="hljs-params">(x){<span class="hljs-keyword">var z = <span class="hljs-string">'console.log("Hello world!")';<span class="hljs-built_in">eval(<span class="hljs-string">'function x(){eval(z)}'); x(); })(<span class="hljs-function">function<span class="hljs-params">() { <span class="hljs-comment">/* ... */ });<br/><br/><span style="font-size: 18pt; background-color: #ff0000"><strong>uglify介绍<br/></strong></span></span></span></span></span></span></span></span></span></span></code></span>
概述:
<br/>
案例:Cesium打包流程,相关技术点和大概流程
原理:代码优化的意义:压缩 优化 混淆
优化:如何完善Cesium打包流程
<br/>
关键字:Cesium gulp uglifyjs
<br/>
字数:2330 | 阅读时间:7min+
<br/>
<br/>
1 Cesium打包流程
<br/>
如果没有记错,Cesium从2016年初对代码构建工具做了一次调整,从grunt改为gulp。作为一名业余选手,就不揣测两者的差别了。个人而言,gulp和Ant的思路很相似,通过管道连接,都是基于流的构建风格,而且gulp更像是JS的编码风格,自带一种亲切感。
<br/>
gulp.task('task1',['task0'], function() {
return fun_task1();
});
<br/>
Task语句是gulp中最常见的,懂了这句话,就等于你看懂脚本了。这句话的意思是,要执行task1,需要先执行task0,而task1的具体工作都在fun_task1方法中。这就是之前说的基于流的构建风格。有了这句话,在命令行中键入:gulp task1,回车执行该指令即可。
<br/>
先安装Node,环境变量等,并安装npm包后,即可使用gulp打包工具,这里推荐cnpm。环境搭建好后,命令行中键入gulp minify开始打包。完整的过程是build->generateStubs->minify。
<br/>
<br/>
Cesium打包流程
<br/>
build:准备工作,创建Build文件夹;将glsl文件转为js形式;最主要的是createCesiumJs方法,遍历Source中所有js脚本,将所有Object记录到Source/Cesium.js;其他的是范例,单元测试相关模块。
<br/>
generateStubs:用于单元测试,略。
<br/>
minify; 首先combineJavaScript主要做了两件事情,打包Cesium和Workers脚本,这是打包的最终结果。Gulp根据指令的不同,比如minify下采用uglify2优化,而combine对应的参数为none,生成路径为CesiumUnminified。
<br/>
另外,细心的人会发现,combineCesium的实现中有这样一句话path.relative('Source',require.resolve('almond')),这是一个小优化,almond是requirejs的精简包,因此,最终的Cesium.js中包含'almond脚本,内置了requirejs的主要方法。
<br/>
如上是Cesium打包的主要流程,简单说主要有3+1类个指令:
<br/>
Clean
清空文件
minify
打包&压缩
combine
只打包,不压缩
JScoverage
单元测试覆盖率,不了解
<br/>
2 代码优化
<br/>
对流程有了一个大概了解,下面,我们详细了解一下uglify2过程都做了哪些代码优化,一言以蔽之,压缩,优化,混淆。
<br/>
uglify2主要有三个参数:-o,-c,-m,-o参数必选,指定输出文件,-c压缩,-m混淆变量名。如下分别为combine、(uglifyjs -o)、(uglifyjs –c -m -o)的文件对比,单位是k:
<br/>
<br/>
uglify2的压缩对比
<br/>
都在一个屋檐下,差距怎么就这么大呢?我们简单说一下从1~2,2~3之间青取之于蓝而胜于蓝的过程。
<br/>
1~2的过程其实很简单,就是干了三件事,去掉注释, 去掉多余的空格(换行符),去掉不必要的分号(;)。就这三件事情,文件一下子小了一半多,换句话就是平时你写的代码有一大半都是废话,此时你旁边的AI程序员可能会喃喃道来“你们人类好愚蠢~”。
<br/>
2~3则是很多小细节的综合应用:
<br/>
去掉一些实际没有调用的函数(Dead code);
将零散的变量声明合并,比如 var a; var b;变为var a,b;
逻辑函数的精简,比如if(a) b(); else c()变为a ? b() : c();
变量名的简化,比如var strObject;变为var s;
……
<br/>
这些小技巧有很多,具体要看不同的压缩工具的考虑优劣,但有些压缩高效的工具并不稳定,可能会破坏语法规范或语意,所以没必要为了几个kb承担过多的风险,目前比较成熟的工具主要有三个uglify2,google closure以及yuicompressor,具体优劣得自己来体会了,我是按照自己的理解给出的先后顺序。最终的效果如下:
<br/>
<br/>
Cesium脚本效果
<br/>
这样的代码只能用单位“坨”来形容了,人类是无法直接读懂的,那浏览器能读懂吗?这是一个好问题!如下是V8引擎对JS语法解析的大概流程:
<br/>
<br/>
V8引擎解析JS脚本
<br/>
下面是在我本机Chrome解析Cesium.js脚本花费时间(脚本从下载完到浏览器解析完的时间差),单位毫秒,因为只测试了一次,可能会有误差,但基本吻合期望值:
<br/>
<br/>
JS脚本解析时间对比
<br/>
首先因为是本机测试,脚本无论是最大的8M还是最小的2.4M,下载速度都很快,因此我们不讨论(但实际应用中要考虑)脚本下载所需时间。
<br/>
其次,如上图,多了一个source,这是源码情况下,这个时间水分比较大,因为是零散的文件,可以做到按需下载,但因为文件比较琐碎,性能也不高。
<br/>
结论是,这种JS脚本优化策略对浏览器的影响不大,浏览器看到优化后的代码,可能会愣一会神,但很快就克服了。
<br/>
3实战
<br/>
知道了代码优化的大概原理,回顾一下代码优化的目的(压缩,优化,混淆),匹配一下结果是否符合期望值。嗯,其一,脚本的大小小了,其二,代码效率也优化了,其三,别人也看不懂了。似乎该做的都已经做了,这个脚本已经很完美了。
<br/>
<br/>
Format后的效果
<br/>
毛爷爷说,与人斗其乐无穷。确实,前两点的目的达到了,但第三点,还差很多。如上,和刚才的脚本是同一个文件,我只是用Chrome的调试工具format而已。这就是理想和现实之间的差距。
<br/>
可见,Cesium默认打包工具在压缩和优化上都没有问题,但在混淆上并不充分,当然Cesium本身是开源的,也没必要搞这些。客观说,JS脚本是明码的,所以反编译只是时间和能力的问题,所以不妨换个态度来看待这个问题,增加反编译的成本,当该成本大于购买成本即可
<br/>
위 내용은 코드 압축 및 난독화 암호화 예시에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!