在 Umi 4 项目中实现 SRI(子资源完整性)功能
背景
在现代前端开发中,确保网站的资源(如 JavaScript、CSS 文件等)未被篡改是非常重要的。子资源完整性(SRI,Subresource Integrity)是一种机制,允许浏览器验证从外部来源加载的资源是否匹配预期的完整性哈希。如果资源被篡改,浏览器将拒绝加载该资源。通过为静态资源添加 SRI 哈希值和 crossorigin="anonymous"
属性,可以提高应用的安全性。
在 Umi 4 项目中,想要启用 SRI 功能,需要对 Webpack 配置做一定的修改。本文将介绍如何在 Umi 4 项目中配置和实现 SRI 功能。
配置步骤
1. 安装依赖
首先,我们需要安装 webpack-subresource-integrity
插件,这是用于生成和处理 SRI 哈希值的核心插件。
npm install webpack-subresource-integrity --save-dev
2. 修改 Umi 4 的 chainWebpack
配置
Umi 4 提供了通过 chainWebpack
配置来扩展 Webpack 配置的能力。在这个配置中,我们将启用 SubresourceIntegrityPlugin
插件,并确保在生产环境下生效。
// config/config.js 或者 .umirc.js 文件
const SubresourceIntegrityPlugin = require('webpack-subresource-integrity');
const HtmlIntegrityPlugin = require('./plugins/htmlIntegrityPlugin'); // 引入自定义的 HTML 处理插件
export default {
chainWebpack: (memo) => {
// 配置输出,设置跨域加载方式
memo.output.crossOriginLoading('anonymous'); // 设置为 anonymous 以启用跨域支持
// 添加 SRI 插件
memo.plugin('subresource-integrity').use(
new SubresourceIntegrityPlugin({
hashFuncNames: ['sha256', 'sha384'], // 使用 sha256 和 sha384 哈希算法
enabled: process.env.NODE_ENV === 'production', // 仅在生产环境启用
})
);
// 生产环境下才启用 HTML 的完整性处理
if (process.env.NODE_ENV === 'production') {
memo.plugin('html-integrity').use(HtmlIntegrityPlugin);
}
}
};
3. 创建 HTML 文件完整性插件
webpack-subresource-integrity
插件将会为生成的资源文件(例如 JS、CSS)添加 integrity
和 crossorigin
属性,但它不直接修改 HTML 文件,因此我们需要自定义一个插件来处理生成的 HTML 文件,手动插入 SRI 哈希。
创建 htmlIntegrityPlugin.js
文件:
// plugins/htmlIntegrityPlugin.js
const fs = require('fs');
const path = require('path');
class HtmlIntegrityPlugin {
apply(compiler) {
compiler.hooks.done.tap('HtmlIntegrityPlugin', (stats) => {
setTimeout(() => {
const assets = stats.toJson().assets || [];
const integrityMap = assets.reduce((acc, asset) => {
if (asset.integrity) {
acc[asset.name] = asset.integrity; // 保存每个资源的完整性哈希
}
return acc;
}, {});
const outputPath = compiler.options.output.path;
const htmlFilePath = path.join(outputPath, '../dist/index.html'); // 获取生成的 HTML 文件路径
if (fs.existsSync(htmlFilePath)) {
let html = fs.readFileSync(htmlFilePath, 'utf-8');
// 遍历资源,替换 HTML 中的标签,添加 integrity 和 crossorigin 属性
Object.entries(integrityMap).forEach(([asset, integrity]) => {
const escapedAsset = asset.replace(/[.*+?^${}()|[]\]/g, '\$&');
const assetName = asset.startsWith('/') ? asset : `/${asset}`;
if (asset.endsWith('.js')) {
const regex = new RegExp(`<script\s+[^>]*src=["']/${escapedAsset}["'][^>]*>`, 'g');
html = html.replace(
regex,
`<script src="${assetName}" integrity="${integrity}" crossorigin="anonymous"></script>`
);
}
if (asset.endsWith('.css')) {
const regex = new RegExp(`<link\s+[^>]*href=["']/${escapedAsset}["'][^>]*>`, 'g');
html = html.replace(
regex,
`<link rel="stylesheet" href="${assetName}" integrity="${integrity}" crossorigin="anonymous">`
);
}
});
// 将修改后的 HTML 文件写回
fs.writeFileSync(htmlFilePath, html, 'utf-8');
}
}, 10000); // 使用 setTimeout 确保在 Webpack 构建完成后再修改 HTML
});
}
}
module.exports = HtmlIntegrityPlugin;
4. 配置生产环境打包(可省略)
确保在生产环境中启用 SRI 功能,因为开发环境通常不会生成完整性哈希值。
在 .umirc.js
或 config/config.js
中,设置环境变量,并确保 SRI 插件和自定义插件仅在生产环境下启用。
// 在 config/config.js 中
export default {
define: {
'process.env.NODE_ENV': process.env.NODE_ENV || 'development', // 确保 NODE_ENV 正确设置
},
chainWebpack: (memo) => {
// 前面提到的 chainWebpack 配置
}
};
5. 测试 SRI 功能
在完成上述配置后,执行构建并检查生成的 HTML 文件,确保所有的资源标签(如 <script>
和 <link>
)都有 integrity
和 crossorigin="anonymous"
属性。
npm run build
检查生成的 index.html
,确保 JS 和 CSS 文件的 <script>
和 <link>
标签包含了 integrity
和 crossorigin
属性。
示例 HTML 片段
<script src="/umi.a813823b.js" integrity="sha256-abc123..." crossorigin="anonymous"></script>
<link href="/umi.a813823b.css" rel="stylesheet" integrity="sha256-xyz789..." crossorigin="anonymous">
总结
通过在 Umi 4 项目中配置 webpack-subresource-integrity
插件并创建一个自定义的 HTML 处理插件,我们可以为生成的静态资源添加 SRI 校验功能,从而提高应用的安全性。这种方法确保了外部资源未被篡改,符合现代 Web 安全最佳实践。
在实施过程中,注意以下几点:
- 正确配置
crossOriginLoading
:确保资源加载时支持跨域。 - 在生产环境中启用 SRI:开发环境通常不需要启用 SRI。
- 自定义插件处理 HTML:Webpack 默认不会修改 HTML 文件,需要通过自定义插件实现对 HTML 中
<script>
和<link>
标签的处理。 - 注意文件输出路径和标签正则匹配:确保资源路径正确匹配,并适当使用
setTimeout
等延时方法确保文件已经生成完毕。
通过这些配置,您可以在 Umi 4 项目中顺利实现 SRI 功能,从而增强应用的安全性和可靠性。
相关issues:github.com/umijs/umi/i…