前言
关于代码混淆网上案例铺天盖地,90%以上都是Webpack
相关来处理代码混淆,对于Vite
的案例少之又少,在解决vite项目代码混淆我花了一些时间来找合适的插件区处理这个问题,最终选择rollup-plugin-obfuscator
。另外第四章节介绍了Webpack
处理代码混淆方案。
npm 地址:链接直达
githup 地址:链接直达
一、插件介绍
rollup-plugin-obfuscator
是 Rollup 构建工具的一个插件,用于对 JavaScript 代码进行混淆和压缩。它的主要作用是增加 JavaScript 代码的安全性和减小生成文件的大小。
1、原理和工作方式:
-
代码混淆:插件通过采用各种技巧来混淆 JavaScript 代码,使其变得难以理解和逆向工程。这些技巧包括变量名重命名、函数提取、字符串替换等。例如,它可以将原始代码中的变量名和函数名改为无意义的名称,从而增加代码的复杂性。
-
代码压缩:除了混淆,插件还可以对代码进行压缩,删除不必要的空格、注释和缩短变量名,以减小生成文件的大小。这有助于加快网页加载速度。
-
选项配置:插件通常提供了许多选项配置,允许开发者自定义混淆和压缩的程度。这些选项包括设置要保留的函数、排除特定文件或模块等。
2、特点:
-
安全性增强:混淆使代码更难以被他人理解,降低了代码被盗用、破解或修改的风险。这对于保护知识产权和敏感数据非常有用。
-
文件大小优化:插件通过代码压缩有助于减小生成文件的大小,从而减少了网络传输时间和提高了网页加载速度。
-
适用于前端项目:
rollup-plugin-obfuscator
特别适用于前端项目,如网页应用程序,以增加客户端端代码的安全性。 -
可定制性:插件通常提供广泛的选项来满足不同项目的需求,开发者可以根据具体情况进行配置。
-
保留功能性:混淆后的代码通常会保留原有的功能性,确保应用程序仍然能够正常运行。
混淆并不是绝对的安全措施,有经验的黑客仍然可能尝试解密代码。因此,在需要更高安全性的情况下,还需要采取其他措施,如服务器端验证和加密。
二、Vite混淆处理
项目环境:Vue3+Vite
1、安装
javascript
yarn add --dev rollup-plugin-obfuscator javascript-obfuscator
或者
npm install --save-dev rollup-plugin-obfuscator javascript-obfuscator
在打包时候还会报错,需要安装下面的依赖
javascript
yarn add javascript-obfuscator -D
2、引入
在vite.config.ts
中引入插件,并进行设置
javascript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import obfuscator from 'rollup-plugin-obfuscator';
export default defineConfig({
// base: "",
build: {
minify: 'esbuild', // 默认
},
esbuild: {
drop: ['console', 'debugger'],//打包去除
},
plugins: [
vue(),
obfuscator({
global:false,
// options配置项实际为 javascript-obfuscator 选项,具体可查看https://github.com/javascript-obfuscator/javascript-obfuscator
options: {
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.75,
numbersToExpressions: true,
simplify: true,
stringArrayShuffle: true,
splitStrings: true,
splitStringsChunkLength: 10,
rotateUnicodeArray: true,
deadCodeInjection: true,
deadCodeInjectionThreshold: 0.4,
debugProtection: false,
debugProtectionInterval: 2000,
disableConsoleOutput: true,
domainLock: [],
identifierNamesGenerator: "hexadecimal",
identifiersPrefix: "",
inputFileName: "",
log: true,
renameGlobals: true,
reservedNames: [],
reservedStrings: [],
seed: 0,
selfDefending: true,
sourceMap: false,
sourceMapBaseUrl: "",
sourceMapFileName: "",
sourceMapMode: "separate",
stringArray: true,
stringArrayEncoding: ["base64"],
stringArrayThreshold: 0.75,
target: "browser",
transformObjectKeys: true,
unicodeEscapeSequence: true,
domainLockRedirectUrl: "about:blank",
forceTransformStrings: [],
identifierNamesCache: null,
identifiersDictionary: [],
ignoreImports: true,
optionsPreset: "default",
renameProperties: false,
renamePropertiesMode: "safe",
sourceMapSourcesMode: "sources-content",
stringArrayCallsTransform: true,
stringArrayCallsTransformThreshold: 0.5,
stringArrayIndexesType: ["hexadecimal-number"],
stringArrayIndexShift: true,
stringArrayRotate: true,
stringArrayWrappersCount: 1,
stringArrayWrappersChainedCalls: true,
stringArrayWrappersParametersMaxCount: 2,
stringArrayWrappersType: "variable",
}
})
]
})
3、配置项解释
下面的列表是 rollup-plugin-obfuscator
插件的配置选项列表,每个选项都用于定制代码混淆和压缩的方式。以下是每个选项的解释:
配置项 | 描述 |
---|---|
global |
这是一个布尔值,如果设置为 false ,将禁用混淆全局作用域的代码。 |
compact |
一个布尔值,如果设置为 true ,则启用代码的紧凑模式,删除不必要的空白字符和注释。 |
controlFlowFlattening |
启用控制流混淆,使代码的控制流程变得更加复杂,从而增加代码的难以理解性。 |
controlFlowFlatteningThreshold |
控制流混淆的阈值,影响混淆程度。 |
numbersToExpressions |
如果设置为 true ,将数字转换为表达式,增加代码的复杂性。 |
simplify |
启用简化,用于删除不必要的代码。 |
stringArrayShuffle |
打乱字符串数组,使字符串更难以理解。 |
splitStrings |
将字符串拆分成小块,增加代码的复杂性。 |
splitStringsChunkLength |
控制字符串拆分的块大小。 |
rotateUnicodeArray |
旋转 Unicode 字符数组,增加代码的混淆度。 |
deadCodeInjection |
开启膨胀,插入死代码,使代码更复杂,阻碍逆向工程。 |
deadCodeInjectionThreshold |
死代码注入的阈值。 |
debugProtection |
如果设置为 true ,将启用调试保护。 |
debugProtectionInterval |
控制调试保护的间隔。 |
disableConsoleOutput |
禁用控制台输出。 |
domainLock |
限制脚本运行的域名列表。 |
identifierNamesGenerator |
控制混淆后的标识符名称生成方式,如 "hexadecimal"。 |
identifiersPrefix |
标识符前缀,可增加混淆度。 |
inputFileName |
输入文件名。 |
log |
是否启用日志记录。 |
renameGlobals |
是否重命名全局变量。 |
reservedNames |
保留的标识符名字列表。 |
reservedStrings |
保留的字符串列表。 |
seed |
用于生成随机数的种子。 |
selfDefending |
如果设置为 true ,将启用自我保护模式。 |
sourceMap |
是否生成源映射文件。 |
stringArray |
是否启用字符串数组混淆。 |
stringArrayEncoding |
字符串数组的编码方式,如 "base64"。 |
stringArrayThreshold |
控制字符串数组混淆的阈值。 |
target |
目标环境,如 "browser". |
transformObjectKeys |
是否转换对象键名。 |
unicodeEscapeSequence |
启用 Unicode 转义序列。 |
domainLockRedirectUrl |
域名锁定重定向的URL。 |
forceTransformStrings |
强制转换字符串的列表。 |
identifierNamesCache |
标识符名称的缓存。 |
identifiersDictionary |
标识符字典,用于自定义标识符名称。 |
ignoreImports |
是否忽略导入的模块。 |
optionsPreset |
预定义的配置选项集,如 "default". |
renameProperties |
是否重命名对象属性。 |
renamePropertiesMode |
对象属性重命名模式,如 "safe". |
sourceMapSourcesMode |
源映射的模式。 |
stringArrayCallsTransform |
是否转换字符串数组调用。 |
stringArrayCallsTransformThreshold |
字符串数组调用转换的阈值。 |
stringArrayIndexesType |
字符串数组索引的类型,如 "hexadecimal-number". |
stringArrayIndexShift |
是否进行字符串数组索引的位移。 |
stringArrayRotate |
是否旋转字符串数组。 |
stringArrayWrappersCount |
字符串数组包装器的数量。 |
stringArrayWrappersChainedCalls |
是否启用字符串数组包装器的链接调用。 |
stringArrayWrappersParametersMaxCount |
字符串数组包装器的参数最大数量。 |
stringArrayWrappersType |
字符串数组包装器的类型。 |
注意,当项目比较庞大时,不建议开启膨胀,也就是deadCodeInjection
取值为false
,不需要设置deadCodeInjectionThreshold
的值
三、Vite混淆处理可能会出现的异常问题
1、Uncaught SyntaxError: Unexpected token '<'
打包之后,部署到服务器后,控制台报错:Uncaught SyntaxError: Unexpected token '<'
。
点开之后,显示如下: 经过查询资料,发现是Vue内部静态资源文件引用出问题了。 出错地方的代码如下:
javascript
let transWorker = new Worker(
new URL("../until/transcode.worker.js", import.meta.url)
);
解决办法:将transcode.worker.js
放到public下的static文件夹下,然后对上面代码修改:
javascript
let transWorker = new Worker(
new URL("/static/transcode.worker.js", import.meta.url)
);
四、webpack混淆处理
Vue2项目中使用Webpack
代码混淆方案使用webpack-obfuscator
+ javascript-obfuscator
。
1、查看Webpack
的版本
先查看Webpack
的版本,不同webpack版本安装的webpack-obfuscator
版本不同。
webpack4.x 使用2.x的 webpack-obfuscator webpack5.x 使用3.x的 webpack-obfuscator
javascript
yarn list webpack
2、安装
- webpack4.x
javascript
yarn add webpack-obfuscator@2.6.0 javascript-obfuscator -D
- webpack5.x
javascript
yarn add webpack-obfuscator javascript-obfuscator -D
3、配置
// vue.config.js
javascript
var obfuscator = require('webpack-obfuscator');
module.exports = {
configureWebpack: {
plugins: [
new obfuscator({
rotateStringArray: true,
/**[] 可以配置 排除混淆的文件 */
}, [])
]
},
}
具体配置项可参考第二章的第三小节。此配置与上面使用Vite几乎相同。
javascript
{
// 压缩,无换行
compact: true,
// 是否启用控制流扁平化(降低1.5倍的运行速度)
controlFlowFlattening: false,
// 应用概率;在较大的代码库中,建议降低此值,因为大量的控制流转换可能会增加代码的大小并降低代码的速度。
controlFlowFlatteningThreshold: 0.75,
// 随机的死代码块(增加了混淆代码的大小)
deadCodeInjection: false,
// 死代码块的影响概率
deadCodeInjectionThreshold: 0.4,
// 此选项几乎不可能使用开发者工具的控制台选项卡
debugProtection: false,
// 如果选中,则会在"控制台"选项卡上使用间隔强制调试模式,从而更难使用"开发人员工具"的其他功能。
debugProtectionInterval: false,
// 通过用空函数替换它们来禁用console.log,console.info,console.error和console.warn。这使得调试器的使用更加困难。
disableConsoleOutput: false,
//锁定混淆的源代码,使其仅在特定域和/或子域上运行。这使得某人只需复制并粘贴您的源代码并在其他地方运行就变得非常困难。
domainLock: [],
//标识符的混淆方式 hexadecimal(十六进制) mangled(短标识符)
identifierNamesGenerator: 'hexadecimal',
//全局标识符添加特定前缀,在混淆同一页面上加载的多个文件时使用此选项。此选项有助于避免这些文件的全局标识符之间发生冲突。为每个文件使用不同的前缀
identifiersPrefix: '',
inputFileName: '',
// 允许将信息记录到控制台。
log: false,
// 是否启用全局变量和函数名称的混淆
renameGlobals: false,
// 禁用模糊处理和生成标识符
reservedNames: [],
// 禁用字符串文字的转换
reservedStrings: [],
// 通过固定和随机(在代码混淆时生成)的位置移动数组。这使得将删除的字符串的顺序与其原始位置相匹配变得更加困难。如果原始源代码不小,建议使用此选项,因为辅助函数可以引起注意。
rotateStringArray: true,
// 混淆后的代码,不能使用代码美化,同时需要配置 cpmpat:true;
seed: 0,
selfDefending: false,
sourceMap: false,
sourceMapBaseUrl: '',
sourceMapFileName: '',
sourceMapMode: 'separate',
// 删除字符串文字并将它们放在一个特殊的数组中
stringArray: true,
// 编码的所有字符串文字stringArray使用base64或rc4并插入即用其解码回在运行时的特殊代码。true(boolean):stringArray使用编码值base64;false(boolean):不编码stringArray值;'base64'(string):stringArray使用编码值base64;'rc4'(string):stringArray使用编码值rc4。大约慢30-50%base64,但更难获得初始值。建议禁用unicodeEscapeSequence带rc4编码的选项以防止非常大的混淆代码。
stringArrayEncoding: false,
// 调整字符串文字将插入stringArray的概率
stringArrayThreshold: 0.75,
// 您可以将混淆代码的目标环境设置为以下之一:Browser;Browser No Eval;Node
target: 'browser',
// 是否启用混淆对象键
transformObjectKeys: false,
// 允许启用/禁用字符串转换为unicode转义序列。Unicode转义序列大大增加了代码大小,并且可以轻松地将字符串恢复为原始视图。建议仅对小型源代码启用此选项。
unicodeEscapeSequence: false
}