使用 grunt-csp 自动生成内容安全策略并注入 HTML

解读

在国内一线前端面试中,“安全左移” 已成必考题。面试官抛出 grunt-csp,表面看是“配个插件”,实则考察三点:

  1. 是否理解 CSP 国内合规要求(工信部《互联网信息服务管理办法》第 15 条、等保 2.0 对 XSS 防护的强制条款);
  2. 能否把安全策略与工程化流程打通,让每次构建都产出可审计的 CSP
  3. 是否具备“插件读源码”能力——grunt-csp 文档久未更新,只有 600 余行代码,却依赖 grunt 事件流与 PostHTML 语法树,需要候选人现场定位注入时机。
    答好此题,等于告诉面试官:我不仅能写业务,还能把安全策略写进流水线,让灰度发布自带安全报告

知识点

  • CSP 指令集:default-src、script-src、style-src、img-src、font-src、connect-src、frame-ancestors、report-uri/report-to;国内必须额外屏蔽 *.gov.cn 与 *.edu.cn 白名单。
  • grunt-csp 原理:基于 PostHTML 解析 HTML → 收集外链 → 哈希 / nonce 计算 → 生成策略字符串 → 通过 grunt.file.write 回写 HTML;不支持多 HTML 入口需自行扩展。
  • Grunt 事件钩子:grunt.task.run 前后可通过 grunt.event.on(‘csp-collect’,fn) 插桩,动态追加埋点域名
  • 国内埋点合规:百度统计 hm.js、神策、GrowingIO 均要求 script-src 显式 ‘unsafe-inline’,但等保测评会扣分,正确做法是用 nonce-{timestamp} 并在服务端同步
  • 性能与缓存:CSP 哈希会随文件变动而变,必须配合 grunt-filerev 在文件名加 hash,否则 CDN 缓存旧策略导致误拦截。
  • 灰度上报:grunt-csp 默认只生成策略,需接 grunt-csp-report 把违规日志推送到阿里云 SLS 或腾讯云 CLS,面试时要主动提及

答案

“我在上一家公司负责金融 H5 项目,等保测评要求所有页面必须携带 Content-Security-Policy 响应头。我们采用 grunt-csp 做构建期自动生成,具体分四步:

  1. 安装与配置
    npm i -D grunt-csp@0.3.2 posthtml posthtml-parser
    在 Gruntfile 中注册任务:
    grunt.initConfig({
    csp: {
    dist: {
    options: {
    ‘default-src’: [“‘self’”],
    ‘script-src’: function (urls, meta) {
    // 国内埋点域名白名单
    return [“‘self’”, “‘unsafe-inline’”, ‘https://hm.baidu.com’,https://sensorsdata.cn’];
    },
    ‘style-src’: [“‘self’”, “‘unsafe-inline’”],
    ‘img-src’: [“‘self’”, ‘data:’, ‘https://static.xxx.com’],
    ‘report-uri’: ‘https://log.xxx.com/csp’
    },
    files: [{ expand: true, cwd: ‘dist’, src: ‘**/*.html’, dest: ‘dist’ }]
    }
    }
    });
  2. 注入方式:grunt-csp 默认把策略写成 <meta http-equiv="Content-Security-Policy" …> 插到 <head> 最前面,避免被前面的 inline script 绕过
  3. 缓存问题:由于文件名带 hash,每次构建后策略串会变,我在 csp 任务之后立刻跑 grunt-filerev,确保 CDN 拿到最新文件名,防止旧策略缓存。
  4. 灰度与回滚:利用 grunt-csp-report 收集浏览器违规日志,上线首小时如拦截率 >0.5% 自动回滚,通过阿里云函数计算关闭 CSP 响应头。
    最终等保测评 XSS 防护项满分,且首屏性能无衰减。”

拓展思考

  1. 如果公司切到 Vite/Rollup,grunt-csp 无法直接复用,可自写 rollup-plugin-csp,思路一致:扫描 bundle 产物 → 生成策略 → 通过 transformIndexHtml 钩子注入。
  2. 国内小程序 WebView 场景,微信内核 86 版本以下不支持 report-to只能降级 report-uri,且长度限制 256 字符,需要把长域名换成短链。
  3. SRI 联动:把 grunt-csp 与 grunt-sri 结合,对第三方 CDN 资源同时生成 integrity 值,在 CSP 里加 require-sri-for script style可堵住运营商缓存投毒
  4. 合规审计:金融类客户要求 “策略可回溯”,我在 CI 里加一步 grunt csp --env=prod --report=json,把策略串写入 csp-release-#{gitSha}.json审计时直接 diff 即可,这也是面试官爱问的“安全可追踪”实践。