一、需求描述

前端项目 / nuxt2项目 打包后,变量值为明文,是没有混淆的内容,这个状态是对的,如果变量的值也混淆了,就失去代码的意义了,但是如何能在一定程度上“密文”显示呢 以增加逆向阅读成本。
二、开发实现梳理
feat(build): 在生产环境启用代码混淆保护
- 引入 webpack-obfuscator 插件
- 仅在生产环境启用代码字符串混淆和控制流扁平化
- 注入死代码以增加反调试难度
- 禁用 console 输出防止日志泄露
- 字符串数组启用 base64 编码和随机旋转
- 设置 75% 的字符串参与混淆处理
三、优缺点分析
用 webpack-obfuscator 对 api 这个单独的 js 文件做代码混淆(不是普通 Terser 压缩),可以控制:字符串加密、控制流平坦化、变量名乱码。
✅ 优点:逆向阅读成本很高,普通开发者打开混淆后的 js 看不懂;
❌ 缺点:
- 只会加密源码里的字符串,浏览器发出请求后,Network 面板依旧完整显示 url;
- 增加打包体积、页面解密耗时;
- 自动生成的 api 文件每次生成后都要参与混淆;
四、哪些内容会被混淆
| 会被混淆 | 不会被混淆 |
|---|---|
| 变量名、函数名 | 中文注释、文档字符串 |
| 字符串字面量(部分) | 正则表达式内容 |
| 控制流结构 | 第三方库的固定文本 |
| 对象属性名(部分) | 国际化/多语言文本 |
五、webpack-obfuscator 完整配置参数
webpack-obfuscator是javascript-obfuscator的 Webpack 封装,核心参数完全相同,额外增加 Webpack 特有选项
javascript-obfuscator 是核心混淆引擎(底层本体);
webpack-obfuscator 是它的 Webpack 适配器 / 插件,专门对接 Webpack 构建流程
| 包 | 定位 | 使用场景 | 调用方式 |
|---|---|---|---|
javascript-obfuscator | JS 混淆核心库,独立工具 | Node 脚本、单独混淆 JS 文件、vite/rollup 等其他构建工具 | API:JavaScriptObfuscator.obfuscate(code, options),也支持 cli 命令行 |
webpack-obfuscator | Webpack Plugin + Loader 包装器 | Webpack 项目打包时自动混淆 chunk/js 模块 | 在 webpack.config.js 的 plugins 里 new WebpackObfuscator (obfuscatorOptions, excludes) |
5.1、核心混淆参数(继承自 javascript-obfuscator)
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
compact | boolean | true | 压缩代码 |
controlFlowFlattening | boolean | false | 控制流平坦化 |
controlFlowFlatteningThreshold | number | 0.75 | 控制流平坦化概率 |
deadCodeInjection | boolean | false | 注入死代码 |
deadCodeInjectionThreshold | number | 0.4 | 死代码注入概率 |
stringArray | boolean | true | 启用字符串数组 |
stringArrayThreshold | number | 0.75 | 字符串加密概率 |
stringArrayEncoding | string[] | [] | 编码:'base64', 'rc4' |
identifierNamesGenerator | string | 'hexadecimal' | 命名方式 |
renameGlobals | boolean | false | 混淆全局变量 |
debugProtection | boolean | false | 调试保护 |
selfDefending | boolean | false | 自我保护 |
disableConsoleOutput | boolean | false | 禁用 console |
domainLock | string[] | [] | 域名锁定 |
reservedNames | string[] | [] | 保留标识符 |
reservedStrings | string[] | [] | 保留字符串 |
5.2、Webpack 特有参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
exclude | string[] | [] | 排除不混淆的 chunk 文件名 |
include | string[] | [] | 只混淆指定的 chunk |
sourceMap | boolean | false | 是否生成 sourceMap |
sourceMapMode | string | 'separate' | sourceMap 模式 |
sourceMapSourcesMode | string | 'sources-content' | sourceMap 来源 |
六、nuxt.config.js代码核心片段
项目环境:Nuxt2+Webpack4
// 安装依赖
yarn add -D webpack-obfuscator@2.6.0
yarn add -D javascript-obfuscator@2.19.1
obfuscator 的语义是"混淆器"
obfuscate(动词):使模糊、使晦涩难懂
obfuscator(名词):执行混淆操作的程序/工具
在代码安全领域,代码混淆器的作用是:
将可读的源代码转换为功能相同但难以阅读和理解的形式
增加逆向工程的难度
保护知识产权和商业逻辑
require('events').EventEmitter.defaultMaxListeners = 50
const path = require('path')
// 尝试加载 webpack-obfuscator,未安装时不影响打包
let WebpackObfuscator = null
try {
WebpackObfuscator = require('webpack-obfuscator')
} catch (e) {
console.warn('[webpack-obfuscator] 未安装,跳过代码混淆')
}
const {env} = process
const isProd = env.MODE === 'prod'
let envFileName = ''
if (['DEV', 'TEST', 'UAT', 'PRE', 'PRD'].indexOf(env.MODE) > -1) {
require('dotenv').config({
path: `.env.${env.MODE}`
})
envFileName = `.env.${env.MODE}`
} else {
require('dotenv').config()
envFileName = '.env'
}
const apiServer = process.env.API_SERVER
const {processenvabcdef} = process.env
if (!isProd) {
console.log(
`当前本地环境变量值: \n apiServer: ${apiServer}`
)
} else {
console.log(`当前线上环境变量值: \n 公共地址 : ${process.env.PUBLIC_PATH}`)
}
const config = {
env: {
'/abc-def/': processenvabcdef,
},
}
let axios = {
proxy: true,
}
// 如果生产指定apiServer, 则使用绝对路径请求api
if (isProd && apiServer) {
axios = {
proxy: false,
baseURL: apiServer,
}
}
module.exports = {
// dir在前,srcDir在后
dir: {
assets: '../m-front-common/pc/assets',
layouts: 'layouts',
middleware: 'middleware',
pages: 'pages',
static: '../m-front-common/pc/static',
store: 'store',
},
srcDir: 'src/',
ssr: false,
env: {
COOKIE_PATH: process.env.COOKIE_PATH || '/',
},
proxy: config.env,
router: {
base: process.env.ROUTER_BASE,
middleware: ['meta', 'auth'],
mode: 'hash',
extendRoutes(routes, resolve) {
if (!(process.env.NODE_ENV === 'prodution') && process.argv.slice(2).includes('--list')) {
require('./m-front-common/extendRoutes').extendRoutes(routes, resolve)
}
},
},
server: {
port: 3001,
host: 'localhost',
},
/*
** Build configuration
*/
styleResources: {
less: '../m-front-common/pc/assets/m-var.less',
},
build: {
publicPath: process.env.PUBLIC_PATH,
extractCSS: isProd,
extend (config, {isDev}) {
config.resolve.alias = config.resolve.alias || {}
const {alias} = config.resolve
alias['@api'] = path.join(__dirname, 'm-front-common/pc/api')
alias.vue = path.join(__dirname, 'node_modules/vue')
// 生产环境通过 loader 方式对 src 目录下的 JS 进行混淆(字符串 base64 加密)
if (!isDev && WebpackObfuscator) {
config.module.rules.push({
test: /\.js$/,
include: [ // 如果不包含范围就是所有文件
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'm-front-common/pc/api'),
path.resolve(__dirname, 'm-front-common/pc/const'),
],
exclude: /node_modules/, // 如果不排除范围就是所有文件
enforce: 'post',
use: {
loader: WebpackObfuscator.loader,
options: {
stringArray: true,
stringArrayEncoding: ['base64'],
rotateStringArray: true,
stringArrayThreshold: 1,
// 关闭最耗时的选项,避免打包卡住
controlFlowFlattening: false,
deadCodeInjection: false,
debugProtection: false,
disableConsoleOutput: true,
},
},
})
} // WebpackObfuscator
},
},
/*
** Headers of the page
*/
head: {
title: '',
meta: [{
charset: 'utf-8'
},
{
name: 'viewport',
content: 'width=device-width, initial-scale=1'
},
{
'http-equiv': 'x-ua-compatible',
content: 'IE=edge, chrome=1'
},
{
hid: 'description',
name: 'description',
content: '',
},
],
script: [],
},
loading: {
color: '#5D81F9',
},
css: [{
src: '../m-front-common/pc/assets/m-reset.less',
lang: 'less',
},],
plugins: [],
modules: [],
axios,
}
七、打包后实现预期

八、哪些需要保留
业务 JS 字符串 ✅ 已 base64 加密
业务 JS 变量名 ✅ 已压缩
.vue的<script>部分 ✅ 已处理webpack publicPath ❌ 不能混淆
外部 CDN URL ❌ 不能混淆
HTML/CSS 里的路径 ❌ 不能混淆
所以不能过度混淆,过度混淆项目就打不开了,如下图

转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/snowball_li/article/details/169264753




