React配置带nonce的CSP时脚本加nonce仍被拦截如何解决
问题根因
拦截是两个配置错误叠加导致的:
- CSP指令存在语法错误:你写的
script-src规则里,unsafe-inline没加单引号,浏览器根本识别不了这是CSP内置关键字,会直接把它当成无效的自定义主机源忽略。虽然现代浏览器在检测到nonce/hash校验规则时会自动忽略'unsafe-inline',但不支持strict-dynamic的老版本浏览器会因为没有有效内联脚本权限,直接拦截React打包产物里的内联runtime引导代码。 - 构建工具没有配置nonce透传:React项目基本都是靠webpack/Rspack这类工具打包,运行时会动态生成三类标签:异步加载chunk的script标签、预加载资源的link标签、内联的runtime脚本。
strict-dynamic规则只对「已经过CSP校验的受信任脚本,通过DOM API动态插入的script标签」生效,管不到link类型的预加载标签,也管不到构建时直接注入HTML的内联脚本;而且webpack默认不会给动态创建的标签自动携带当前页面的nonce,这些没带nonce的标签会直接被CSP拦下,最后表现为React打包的JS加载失败。
修复方案
按顺序调整配置即可:
- 修正CSP语法错误
把script-src里漏了单引号的unsafe-inline改成'unsafe-inline',修正后的CSP参考:
这里的Content-Security-Policy: script-src 'strict-dynamic' 'nonce-{0}' 'unsafe-eval' 'unsafe-inline''unsafe-inline'只是给不支持strict-dynamic的老浏览器做降级兼容,现代浏览器识别到nonce规则后会自动忽略这个配置,不会拉低安全等级。 - 配置构建工具全局透传nonce
在React项目入口文件(一般是src/index.js)的最顶部加如下代码,让webpack给所有动态创建的script、link标签自动带上nonce:
服务端渲染模板时,在HTML的head里加对应的meta标签存放每次请求生成的随机nonce:// 读取服务端注入的nonce值,不要硬编码在前端代码中 const nonce = document.querySelector('meta[name="csp-nonce"]').content; // 配置webpack全局nonce __webpack_nonce__ = nonce;<meta name="csp-nonce" content="@nonce" /> - 检查内联脚本配置
如果你在构建配置里开了runtimeChunk: 'inline'(把webpack runtime代码内联到HTML中),要给这段内联script标签也加上匹配的nonce属性,不能遗漏。
如果是用Create React App搭建的项目、没有eject配置,可以通过craco/react-app-rewired等自定义配置工具,配合csp-html-webpack-plugin实现构建时自动给所有script/link标签注入nonce,不用手动修改每个标签。
配置完可以打开浏览器控制台看CSP报错,如果还有拦截,根据报错提示的资源路径排查遗漏的未加nonce的标签即可。
内容的提问来源于stack exchange,提问作者Gal Algirdas
相关产品推荐
相关产品推荐

