【技术推荐】前端JS攻防对抗
字数 1109 2025-08-27 12:33:23
前端JS攻防对抗技术详解
1. 爬虫与反爬虫发展概述
1.1 攻防发展阶段对比
| 阶段 | 攻击手段 | 防御手段 |
|---|---|---|
| 1 | 简单脚本编写爬虫(Python、Java等) | 检测User-Agent、HTTP Headers区分机器人 |
| 2 | 添加正常浏览器请求头部伪装用户 | 检测IP访问频率,封禁异常IP |
| 3 | 使用IP代理池、秒拨等技术 | 网站重构,使用Ajax动态传输数据 |
| 4 | 分析数据传输接口直接获取数据 | 添加验证码、JS参数加密 |
| 5 | 深度学习破解验证码、JS调试破解参数加密 | 寻求第三方安全产品 |
1.2 现代反爬虫体系
- 访问令牌(身份认证)
- 验证码(滑动、逻辑、三维等)
- 行为&指纹检测(人机区分)
- 请求&响应加密
2. JS防破解技术
2.1 代码混淆技术
2.1.1 布局混淆
无效代码删除
- 删除注释文本
- 删除调试信息
- 删除无用函数和数据
- 删除缩进和换行符
标识符重命名
- 单字母或简单组合
var animal = 'shark' → var a = 'shark'
- 十六进制命名
var animal = 'shark' → var _0x616e696d616c = 'shark'
- 作用域内标识符碰撞
function _$QQO(){
var _$QQO,
}
垃圾代码添加
- 插入大量无意义代码混淆视听
2.1.2 数据混淆
数字混淆
233 → 0351(八进制) → 0xe9(十六进制) → 0b11101001(二进制)
字符串混淆
- 十六进制
'shark' → '\x73\x68\x61\x72\x6b'
- Unicode编码
'shark' → '\u0073\u0068\u0061\u0072\u006b'
- 转字节数组
function stringToByte(str){
var bytearr = [];
for(var i = 0; i < str.length; i++){
bytearr.push(str.charCodeAt(i));
}
return bytearr
}
stringToByte('shark') → [115, 104, 97, 114, 107]
- Base64编码
'shark' → 'c2hhcms='
数组混淆
- 元素引用和顺序打乱
var arr = ['log', 'Date', 'getTime']
console[arr[0]](new window[arr[1]]()[arr[2]]())
// 打乱数组元素
(function(arr, num){
var shuffer = function(nums){
while(--nums){
arr.unshift(arr.pop());
}
};
shuffer(++num);
}(arr, 0x10))
布尔值混淆
!undefined → true
!null → true
!0 → true
!NaN → true
!"" → true
!{} → true
→ true
2.1.3 控制混淆
控制流平坦化
原始流程:
function source(){
var a = 1;
var b = a + 10;
var c = b + 20;
var d = c + 30;
var e = d + 40;
return e;
}
平坦化后:
function controlflow(){
var controlflow_seq = '5|3|4|2|1|6'.split('|'), i = 0
while(!![]){
switch(controlflow_seq[i++]){
case '1': var e = d + 40; continue;
case '2': var d = c + 30; continue;
case '3': var b = a + 10; continue;
case '4': var c = b + 20; continue;
case '5': var a = 1; continue;
case '6': return e; continue;
}
break;
}
}
逗号表达式
function source(){
var a, b, c, d, e;
return a = 1, b = a + 10, c = b + 20, d = c + 30, e = d + 40, e
}
// 或
function source(){
var a, b, c, d, e;
return e = (d = (c = (b = (a = 1, a + 10), b + 20), c + 30), d + 40);
}
2.2 混淆工具
在线混淆工具
AST(抽象语法树)处理
- 使用Babel等工具进行语法树转换
- AST处理控制流平坦化示例:
// 还原思路:
// 1. 获取分发器生成的顺序
// 2. 把分支语句和条件对应生成case对象
// 3. 利用分发器顺序从case对象获取case
// 4. 输出还原后的代码
const visitor = {
WhileStatement(path){
let {body} = path.node;
let switch_statement = body.body[0];
// ... 详细处理逻辑
path.replaceWithMultiple(tmp_array);
path.scope.crawl();
}
}
traverse(ast, visitor);
3. 反调试技术
3.1 控制台防护
阻止控制台打开
window.addEventListener('keydown', function(event){
if(event.key == "F12" || ((event.ctrlKey || event.altKey) &&
(event.code == "KeyI" || event.key == "KeyJ" || event.key == "KeyU"))){
event.preventDefault();
return false;
}
});
window.addEventListener('contextmenu', function(event){
event.preventDefault();
return false;
});
窗口尺寸检测
(function(){
'use strict';
const threshold = 160;
setInterval(() => {
const widthThreshold = window.outerWidth - window.innerWidth > threshold;
const heightThreshold = window.outerHeight - window.innerHeight > threshold;
if(widthThreshold || heightThreshold){
emitEvent(true, orientation);
}
}, 500);
})();
3.2 调试器防护
定时debugger
function debug(){
debugger;
setTimeout(debug, 1);
}
debug();
时间差检测
addEventListener("load", () => {
var threshold = 500;
const measure = () => {
const start = performance.now();
debugger;
const time = performance.now() - start;
if(time > threshold){
document.write("<p>DevTools were open since page load</p>");
}
}
setInterval(measure, 300);
});
3.3 控制台输出防护
清空控制台
function clear(){
console.clear();
setTimeout(clear, 10);
}
clear();
3.4 断点调试防护
时间检测断点
var timeSinceLast;
addEventListener("load", () => {
var threshold = 1000;
const measure = () => {
if(!timeSinceLast){
timeSinceLast = performance.now();
}
const diff = performance.now() - timeSinceLast;
if(diff > threshold){
document.write("<p>A breakpoint was hit</p>");
}
timeSinceLast = performance.now();
}
setInterval(measure, 300);
});
scope检测
function malicious(){
const detect = (function(){
const dummy = /./;
dummy.toString = () => {
alert('someone is debugging the malicious function!');
return 'SOME_NAME';
};
return dummy;
}());
}
3.5 事件调用防护
堆栈检测
function test(){
console.log(new Error().stack);
// 或
try{throw new Error('');}catch(error){stack = error.stack || '';}
console.log(stack);
}
3.6 函数属性防护
函数防劫持
function hijacked(fun){
return "prototype" in fun ||
fun.toString().replace(/\n|\s/g,"") != "function"+fun.name+"(){[nativecode]}";
}
// 设置不可修改
Object.defineProperty(window, 'eval', {
writable: false,
configurable: false,
enumerable: true
});
3.7 NodeJS调试防护
代码格式化检测
function y(){
var t = (function(){
var B =!![];
return function(W, i){
var F = B ? function(){
if(i){
var g = i['apply'](W, arguments);
i = null;
return g;
}} : function(){};
B =);
var l = t(this, function(){
return l['toString']()['search']('( )['toString']()['constructor'](l)['search']('( );
});
l();
console['log']('aaaa');
console['log']('ccc');
};
y();
4. 高级防护手段
- 自定义编译器
- WebAssembly技术
- 浏览器特性挖掘
- 结合业务场景的定制化混淆方案
5. 总结
前端JS防破解需要结合代码混淆和反调试技术,根据业务特点选择合适的防护策略:
- 代码混淆:从布局、数据、控制三个层面进行混淆,增加代码阅读难度
- 反调试:防止动态调试分析,保护关键逻辑不被轻易发现
- 组合使用:多种技术组合使用,形成防御体系
- 性能平衡:在安全性和性能之间找到平衡点
通过合理运用这些技术,可以有效提高前端代码的安全性,防止恶意分析和爬虫攻击。