TOP ⬆

Webpack 提升前端安全的方法

提升 CSP 安全的 nonce 方法

CSP:内容安全策略,浏览器通过该策略可以拒绝内联脚本和外部脚本的注入,有效防御 XSS 的反射和存储攻击

在 webpack 中,可以通过 __webpack_nonce__ 在运行时赋值的方法将可信的脚本都插入一个单次随机码,每次请求时,可以和服务器配合下发 nonce 码,这样浏览器在 CSP 策略下就能将安全的脚本和外部脚本进行区分。

简单描述一下流程:

  1. 服务器生成 nonce 并注入到响应的 HTML 中
  2. 浏览器执行 HTML 并赋值全局变量
  3. webpack 运行时获取到全局变量的 nonce
  4. webpack 将内部脚本全部插入 nonce
  5. 浏览器执行 CSP 策略进行验证

nonce 注入过程

服务器生成 nonce

常用的服务器有三种方式,express/koa 启动服务、nginx 启动服务、webpack-dev-server 开发服务器。

首先提供一个 HTML 的基础模板来给 webpack 运行时提供一个 nonce

<html>
<head>
    <meta http-equiv="Content-Security-Policy" content="script-src 'nonce-<%= nonce %>'; style-src 'nonce-<%= nonce %>';">
</head>
<body>
    <script nonce="<%= nonce %>">
        window.__NONCE__ = '<%= webpackNonce %>';
        window.__webpack_nonce__ = '<%= webpackNonce %>';
    </script>
    <div id="app"></div>
</body>
</html>

服务器的核心作用就是在 HTML 的这个模板中注入 nonce 变量,这样浏览器就可以知道 CSP 的应用范围。

同时,根据这个策略我们也可以知道 nonce 的更新规则,核心就是向服务器重新请求资源的时候需要会更新 nonce(以单页面应用为例)

  1. 刷新页面、打开新标签页的时候会更新 nonce;

  2. 前进、回退页面的时候会更新 nonce;

  3. SPA 的页面路由跳转不会更新 nonce;

  4. AJAX 动态内容加载不会更新 nonce;

webpack 如何使用 nonce

注入 nonce 是 webpack 的运行时的内置功能,因此无需任何配置就能默认开启,激活的条件就是入口文件的声明是否有 __webpack_nonce__ 变量的赋值

// src/index.js 入口文件
__webpack_none = windows.__NONCE__; // 这里可以通过服务器生成 HTML 中携带一个 script 在全局注入服务器生成的 nonce
// __webpack_none = 'DEV_DEV_DEV' // 开发模式下可以固定一个值,但生产环境坚决不能固定,否则无效

nonce 能配合 CSP 防御 XSS 的原理

所以即使 nonce 是明文形式展现在前端的,但由于其随机生成以及单次使用即过期的原理,还是能防御大部分 XSS 攻击。

生成 nonce 的代码展示

对外部资源启用的 SRI 限制*

SRI 的核心目标是在引用外部的脚本或者样式时,对外部资源进行哈希校验,避免 CDN 或者第三方对资源进行篡改。

webpack 可以通过插件在构建时就将对应的资源 hash 注入到 HTML 的 <script> <link> 标签上

// webpack.config.js
const { SubresourceIntegrityPlugin } = require('webpack-subresource-integrity');

module.exports = {
  output: {
    path: __dirname + '/dist',
    filename: '[name].[contenthash].js',
    crossOriginLoading: 'anonymous', // required for SRI,为了帮助浏览器在跨域加载时做验证,这里需要保证 crossinging 上有 `anonymous` 字段
  },
  plugins: [
    new SubresourceIntegrityPlugin({
      hashFuncNames: ['sha384']
    })
  ]
};

SRI 防御的核心策略

  1. 可信的内部资源:在 <script> 或者 <link> 的标签上写上对应资源的 integrity="算法-Base64Hash"
  2. 可以校验的外部资源:从 CDN 下载资源后,浏览器通过哈希校验即可得资源是否被篡改

SRI 策略可以防御的和无法防御的

其他策略(针对 XSS)

防范 CSRF 主要需要后端配合,使用临时 token 、同源 cookie 等方法,这里主要讨论 XSS 攻击的防御

更简单的 source-map 避免使用 eval/new Function

防范 HtmlWebpackPlugin 及 HTML 模板 的注入风险

不暴露敏感信息到前端业务(防范 DefinePlugin 的使用)