We recently upgraded our Application to Rails 5.2. We are also using Turbolinks (together with the Rails Engine) and RailsUJS.
With Rails 5.2 we have new DSL for CSP (Content security policy). It is configured like this in initializers/content_security_policy.rb
:
Rails.application.config.content_security_policy do |policy|
policy.object_src :none # disallow <object> tags (Good-bye Flash!)
policy.default_src :self, :https
policy.font_src :self, :https, :data, Rails.configuration.application.asset_host, Rails.configuration.application.aws_s3_media_cdn
policy.img_src :self, :https, :data, Rails.configuration.application.asset_host, Rails.configuration.application.aws_s3_media_cdn
policy.script_src :self, :https, Rails.configuration.application.asset_host
policy.style_src :self, :https, Rails.configuration.application.asset_host
if Rails.env.development? || Rails.env.test?
# Fix for webpack-dev-server and ActionCable
policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035", "ws://localhost:3000"
end
# Specify URI for violation reports
# policy.report_uri "/csp-violation-report-endpoint"
end
# If you are using UJS then enable automatic nonce generation
Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16)
This works fine. But I cannot get Turbolinks and RailsUJS to work together.
In app/views/layouts/application.html.haml
we have (simplified):
!!!5
%html
%head
= csrf_meta_tags
= csp_meta_tag
= javascript_pack_tag 'application', 'data-turbolinks-track': :reload
= action_cable_meta_tag
%body
= yield
Assuming we have a ujs button like this:
<a class="btn btn-danger" data-disable-with="Processing..." data-params="device%5Bstatus%5D=inactive" data-remote="true" rel="nofollow" data-method="patch" href="/devices/1">Shutdown</a>
And a controller looking very simple:
class DevicesController < ApplicationController
respond_to :html
#...
def update
@device.update(device_params)
respond_with @device, location: :devices
end
end
This works fine and we get a proper response in browser coming from the Turbolinks rails engine:
Turbolinks.clearCache()
Turbolinks.visit("http://localhost:3000/devices", {"action":"replace"})
But we get the error:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' https: 'nonce-QAz+FlHz5wo0IwU5sIMZ/w==' 'nonce-IsrK1b0jw1w7cqRhHeZ7ug==' 'nonce-Rpl8hMBgap79cfwdlXXwjA==' 'nonce-1Wq7MbBEYMDCkEWGexwQ9Q==' 'nonce-EUL22iiKHn0hkNuW3fpkbA==' 'nonce-F5Vg50g0JvAvkXHHu+p0qw==' 'nonce-slHxjCy9VVEvvoIcJ870lg==' 'nonce-lboTgbdLG4iCgUozIK4LPQ==' 'nonce-K9yAPOgjZDXRTvnJb3glTA==' 'nonce-ux2kfUZjU/nxJn30LaTFjQ==' 'nonce-8E8cTAX+jWNpvl5lw0Ydjw==' 'nonce-BvJ4wU3AqjZRWY930+W8kg==' 'nonce-PsS01n7AnjmiThKQJFzUBA==' 'nonce-RCoOSLXbx6Cj8aw+LuBSwA==' 'nonce-o5MfDl/crSPzjSyMzIvXNA==' 'nonce-s8NPaOETMpU2f48LR2SuqQ==' 'nonce-Omuo2P68l09PTBFxmk4DkA==' 'nonce-N3YQfaIuPSrURB8jhVz3Sw==' 'nonce-Ts4Bp4GUqawLcHI1mRLcxw==' 'nonce-fTZ6W2u9eh8K5yCJMPfJGg==' 'nonce-1ST0058rq41fDhw8CforxA==' 'nonce-tA+jUJ1x841ZseUUjvQn9w==' 'nonce-CVjBLiByDSqBNHdG6/izBA==' 'nonce-1z6mH6xtPajsxVmojM8SNA==' 'nonce-0zlDfL8I0go9aII/DGZUzg==' 'nonce-WOrw4qdxeKfZQ1R7xUfdbQ==' 'nonce-G8kmjJA5E35Asgy6mj80PQ=='". Either the 'unsafe-inline' keyword, a hash ('sha256-9KVlOPCQBe0v+kIJoBA6hi7N+aI2yVDUXS9gYk4PizU='), or a nonce ('nonce-...') is required to enable inline execution.
So the first question here: Where are those many nonces coming from?
Another investigation: After I call Turbolinks.visit in the javascript console, the csp-nonce
and csrf-token
values are changing. I think that has to be the problem. Since Rails UJS uses the original value to add nonce to the inline javascript tag it creates. But after everything gets rendered the nonce has changed. So the inline tag is no longer valid.
How can I avoid this?
-- NOTE: We are setting up Turbolinks and RailsUJS via webpacker like this:
import Turbolinks from 'turbolinks'
import Rails from 'rails-ujs'
// Start application
Rails.start(); // Rails ujs
Turbolinks.start(); // Turbolinks, obviously...
Thanks in advance,
spa
Follow up:
The multitude of nonces was a bug in rails that has been corrected in Rails 5.2.1.