CSP中的strict-dynamic有什么用?为什么我的动态脚本还是被阻止了?
我在给项目配置CSP时用了’strict-dynamic’,但页面加载时动态创建的script标签还是报blocked错误。比如这样:
const script = document.createElement('script');
script.src = '/assets/dynamic.js';
document.head.appendChild(script);
CSP头设置的是:
Content-Security-Policy: default-src 'none'; script-src 'strict-dynamic' 'nonce-123ABC' https://cdn.example.com;
明明加了strict-dynamic应该允许动态脚本啊?尝试过把nonce加在script元素上也没用,控制台还是提示违反CSP策略。是不是哪里配置错了?
strict-dynamic的作用是允许由可信脚本动态创建的脚本执行,但前提是创建它的那个“父脚本”本身必须是被CSP信任的。你当前的问题在于创建动态的脚本本身没有被CSP信任,所以即使加了strict-dynamic也没用。你的CSP配置:
只信任带有nonce="123ABC"的脚本和来自https://cdn.example.com的脚本,但创建动态script的那段JS代码并没有被标记为可信。
你需要确保创建脚本的代码本身是被信任的,比如:
或者外部脚本需要来自https://cdn.example.com,并且加载时带有正确的nonce。
否则,哪怕用了
strict-dynamic,浏览器也会拦截这个动态脚本的加载和执行。总结一下:
1.
strict-dynamic不是万能的,必须从“可信源”创建出来的脚本才能被信任2. 创建脚本的父脚本必须在CSP中被认为是可信的(通过nonce或者外部域名)
3. 动态添加的script标签本身不需要加nonce,关键是创建它的脚本要可信
复制这个配置思路:
并确保创建script的代码是带有nonce的那个脚本块或者来自https://cdn.example.com。
你现在的代码需要改两个地方:
1. 创建script元素时需要加上nonce属性
2. nonce值要和CSP头里的nonce完全一致
应该改成这样:
不过要注意,script.nonce这个属性是只读的,在有些浏览器环境可能无法直接赋值。我当时为了兼容性最后还是改成了用base64内联脚本,或者你也可以考虑改用unsafe-hashes配合hash值来实现。
还有一点容易忽略的是,如果你的dynamic.js里又动态创建了新脚本,那新脚本会继承父级的nonce值。如果这时候父级脚本没有nonce,就会继续报错。这种情况需要给所有层级的动态脚本都加上nonce属性。