开发环境下Content Security Policy导致Rails错误页面失效
我的Rails应用已启用Content Security Policy(CSP),本地开发环境运行时,Rails错误页面因浏览器CSP阻止内联JavaScript和CSS无法正常显示。Chrome中turbo.es2017-esm.js报出如下错误:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' localhost:3000 'nonce-vJgPXpdJcQ60SidkuK6V/Q=='". Either the 'unsafe-inline' keyword, a hash ('sha256-N8P082RH9sZuH82Ho7454s+117pCE2iWh5PWBDp/T60='), or a nonce ('nonce-...') is required to enable inline execution.
查看页面元素发现错误页面的script标签nonce属性为空:
<script type="text/javascript" data-template="error_page" nonce="">
当前配置文件
config/initializers/content_security_policy.rb
Rails.application.configure do # The policies are set in config/environments/[development/test/production].rb # Generate random nonce that will be used in <script> and <style> elements config.content_security_policy_nonce_generator = -> (request) { SecureRandom.base64(16) } # Add the nonce to the Content-Security-Policy header config.content_security_policy_nonce_directives = %w(script-src style-src) end
config/environments/development.rb
config.content_security_policy do |policy| policy.default_src :self policy.img_src :self, 'data:' policy.script_src :self policy.style_src :self policy.connect_src :self end
版本信息
- Rails: 7.0.4.2
- turbo-rails: 1.3.3
- JavaScript依赖:@hotwired/turbo-rails:7.2.5、@hotwired/turbo:7.2.5、@hotwired/stimulus:3.2.1
解决办法
1. 自定义错误页面布局,确保nonce注入
Rails默认错误页面渲染时未经过常规视图流程,导致nonce未正确赋值。可以创建自定义错误布局:
在app/views/layouts/下新建error.html.erb,参考主布局结构并确保脚本标签使用content_security_policy_nonce辅助方法:
<!DOCTYPE html> <html> <head> <title>Error</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <script nonce="<%= content_security_policy_nonce %>" src="<%= asset_path 'turbo.es2017-esm.js' %>" type="module"></script> </head> <body> <%= yield %> </body> </html>
2. 临时放宽开发环境CSP规则(仅限开发)
如果不想自定义布局,可以在开发环境CSP中添加unsafe-inline(生产环境禁止使用),或者使用错误页面内联脚本的哈希值:
方案A:添加unsafe-inline
修改config/environments/development.rb:
config.content_security_policy do |policy| policy.default_src :self policy.img_src :self, 'data:' policy.script_src :self, 'unsafe-inline' policy.style_src :self, 'unsafe-inline' policy.connect_src :self end
方案B:使用脚本哈希值
从Chrome错误提示中提取哈希值,添加到script-src:
config.content_security_policy do |policy| policy.default_src :self policy.img_src :self, 'data:' policy.script_src :self, 'sha256-N8P082RH9sZuH82Ho7454s+117pCE2iWh5PWBDp/T60=' policy.style_src :self policy.connect_src :self end
3. 验证nonce生成逻辑
在初始化器中添加日志,确认错误请求是否生成nonce:
修改config/initializers/content_security_policy.rb:
config.content_security_policy_nonce_generator = -> (request) { nonce = SecureRandom.base64(16) Rails.logger.debug "Generated nonce: #{nonce} for request path: #{request.path}" nonce }
重启服务器后,查看日志确认错误请求(如/500)是否有nonce生成记录。
内容的提问来源于stack exchange,提问作者Evgenii

