公司信安部门对项目进行安全扫描,查出一些漏洞,其中有一项要求升级 javascript 框架库(如图):
吓得我以为让我把 Vue2 升级成 Vue3。
经过一番询问后才知道,是工具包中依赖的 YUI 是存在安全漏洞的版本。
信安同事一开始只给我上面的图,我根本不知道是哪个包的版本需要升级。
我只能用漏洞标题去百度,搜到了一些同样扫出漏洞的情况,多是在说下面几个工具:
查到这里,同事也发来更详细的说明:
YUI:2.9.0 (Link) https://www.cvedetails.com/cve/CVE-2012-5883/
于是我就在打包后的代码中搜索 YUI
(不区分大小写,不进行全字匹配),果然搜到了一段注释:
确认了这个事情,接下来就容易多了。这明显不是我的代码,那就在 node_modules
中继续搜索,最终在 jsencrypt
下查到了这段注释:
在网上搜索关于 YUI 2.9.0 漏洞的信息:搜索结果
结果中也包含扫描工具提供的漏洞地址,该漏洞指出:
在 YUI 2.8.0 - 2.9.0 中的 Flash 组件基础机构中存在 XSS 漏洞,远程攻击者可以通过 SWF 程序(YUI SWF 提供了一种在网页中嵌入 Adobe Flash Player 的标准方法)注入任意 Web 脚本或 HTML。
现在我们知道 YUI 2.9.0 这个版本确实有安全隐患,BUG 我们真的有这个漏洞么 ?
官方介绍:YUI 是一个免费的、开源的 JavaScript 和 CSS 库,用于构建丰富的交互式 web 应用程序。
不太严谨的说,YUI 就是类似 jquery 的一个工具库。
然后看看 jsencrypt
为啥要使用它呢?
从 node_modules\jsencrypt\lib\lib\jsrsasign\yahoo.js
的代码看,整个文件到处的只是一个包含 lang.extend
方法的对象。
lang.extend
方法模拟类的继承方式,从一个对象上扩展出领域给对象。
也就是说 jsencrypt
仅仅使用了 YUI 的 lang.extend
方法,碰都没碰 YUI SWF。
这么看尽管代码用的是 2.9.0 版本的一部分,但是这个版本的安全漏洞根本影响不到项目。
现在可以总结出:
那其实只要解决掉 YUI 版本号的这块注释即可。
网上搜的办法大部分是使用压缩后(不含注释)的文件:node_modules\jsencrypt\bin\jsencrypt.min.js
// 旧的引入方式
import JSEncrypt from 'jsencrypt'
// 新的引入方式
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
我的办法是,在打包环节删除注释,全局 AOE 干掉所有注释,解决注释引发的所有问题。
包括安全扫描出的,包含外部平台链接、包含邮箱 URL(npm 作者邮箱)之类的。
我是用 Vue Cli 生成的 Vue2 版本的项目,默认打包配置中 webpack 使用了 terser (terser-webpack-plugin)进行代码优化,直接进行配置即可。
inspect
看下默认配置:
optimization: {
...
minimizer: [
{
options: {
test: /\.m?js(\?.*)?$/i,
chunkFilter: () => true,
warningsFilter: () => true,
extractComments: false,
sourceMap: false,
cache: true,
cacheKeys: defaultCacheKeys => defaultCacheKeys,
parallel: true,
include: undefined,
exclude: undefined,
minify: undefined,
terserOptions: {
output: {
// 默认保留了 @license 注释
comments: /^\**!|@preserve|@license|@cc_on/i
},
compress: {...},
mangle: {...}
}
}
}
]
},
只需要修改 output
配置即可,配置项参考:
Format options
vue.config.js
增加配置:
chainWebpack(config) {
// 删除注释
config.optimization.minimizer('terser').tap(args => {
// 直接修改 terserOptions 下的属性值,保留原有配置
// 这里访问 terserOptions 的时候并没有 output,访问不到 output.comments 需要直接赋值
args[0].terserOptions.output = {
comments: false,
}
return args
})
}