Hanami 2.2配置CSP nonce策略后,AJAX返回的JavaScript响应无法执行的问题求助
我现在在维护一个基于Hanami 2.2的Web应用,已经在config/app.rb里用nonce策略配置了内容安全策略(CSP),允许内联脚本执行:
config.actions.content_security_policy[:script_src] = "'self' 'nonce'"
页面加载时,通过Hanami的stylesheet_tag和javascript_tag生成的资源标签都会自动带上nonce,浏览器能正常加载CSS和JS资源,页面源码片段如下:
<html lang='en'> <head> <meta content='text/html; charset=UTF-8' http-equiv='Content-Type'> <meta charset='UTF-8'> <meta content='width=device-width, initial-scale=1.0' name='viewport'> <meta name="csrf-param" content="_csrf_token"> <meta name="csrf-token" content="4111c46fb6a45da9dfea2ccd8521256f7f1c3852fbedc890d2ed56dc4111b7c9"> <link href="/assets/app.css" type="text/css" rel="stylesheet" nonce="iWzsbPAkiB//CnB0"> </head> <body> <!-- 页面内容 --> <script src="/assets/app.js" type="text/javascript" nonce="iWzsbPAkiB//CnB0"></script> </body> </html>
现在遇到了一个棘手的问题:我有个页面上的简单表单(包含两个文本输入框),需要通过jQuery AJAX提交,并且希望服务器返回的JS代码能直接执行。我在app/templates/home/create.js.haml里写了这样的代码:
%script{ nonce: content_security_policy_nonce } :plain alert("Hello");
这个逻辑和Rails里返回JS响应的思路类似,但实际测试时出了问题:
- AJAX请求的头信息正常,包含
Accept: */*和Content-Type: application/x-www-form-urlencoded; charset=UTF-8 - 服务器返回的响应头里,CSP的
script-src也正确带上了对应的nonce:Content-Security-Policy: base-uri 'self';child-src 'self';connect-src 'self';default-src 'none';font-src 'self';form-action 'self';frame-ancestors 'self';frame-src 'self';img-src 'self' https: data:;media-src 'self';object-src 'none';script-src 'self' 'nonce-iWzsbPAkiB//CnB0';style-src 'self' 'unsafe-inline' https: Content-Type: application/javascript; charset=utf-8 - 响应体里的内容也正确带上了和CSP一致的nonce:
<script nonce='iWzsbPAkiB//CnB0'> alert("Hello"); </script>
但浏览器就是不执行这段脚本,控制台里报CSP错误:
Content-Security-Policy: The page’s settings blocked an inline script (script-src-elem) from being executed because it violates the following directive: “script-src 'self' 'nonce-iWzsbPAkiB//CnB0'”
明明nonce完全一致,为什么还是被拦截了?
我还测试了两种其他情况:
如果把CSP改成允许
unsafe-inline:config.actions.content_security_policy[:script_src] = "'self' 'unsafe-inline'"同时把
create.js.haml改成直接写脚本内容::plain alert("Hello");这时响应体里只有
alert("Hello");,脚本能正常执行,但unsafe-inline相当于放弃了CSP的安全防护,我不想用这种方案。如果用Haml的
:javascript标签来写::javascript alert("Hello");响应体里会生成不带nonce的
<script>标签,而且浏览器会报语法错误:Uncaught SyntaxError: expected expression, got '<'
我已经查了不少相关问题,尝试了各种社区建议但都没解决问题。现在实在没思路了,有没有大佬能告诉我接下来该排查什么?
内容来源于stack exchange

