New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handling CSP nonce for scripts between Turbo.visit #294
Comments
|
Hi everyone, I am just going to post how I solved this with Turbo Drive in my Rails app and also make it work with any legacy UJS. This is a port of similar work I did for the original Turbolinks. If you want UJS, Turbo, and other inline nonced JS to work you need to do the following:
# In config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy_nonce_generator = -> (request) do
# use the same csp nonce for turbo requests
if request.env['HTTP_TURBO_REFERRER'].present?
request.env['HTTP_X_TURBO_NONCE']
else
SecureRandom.base64(16)
end
end
// Put this somewhere in /app/javascript
document.addEventListener('turbo:before-fetch-request', (event) => {
// Turbo Drive does not send a referrer like turbolinks used to, so let's simulate it here
event.detail.fetchOptions.headers['Turbo-Referrer'] = window.location.href
event.detail.fetchOptions.headers['X-Turbo-Nonce'] = $("meta[name='csp-nonce']").prop('content')
})
// Put this somewhere in /app/javascript
document.addEventListener("turbo:before-cache", function() {
let scriptTagsToAddNonces = document.querySelectorAll("script[nonce]");
for (var element of scriptTagsToAddNonces) {
element.setAttribute('nonce', element.nonce);
}
});If done correctly you should not have any error messages about inline nonced JS not running due to your CSP policy. Keep in mind that while this approach works, this arguably weakens the security of the nonce generation because a single nonce could last for a long time for your average visitor (until they do a normal non-turbodrive navigation or reload the page). That said, IMO it's much better to have this, than to have no CSP at all. |
|
edit: doesn't fix my issue with Safari when injecting scripts in the without the referrer and without jQuery: document.addEventListener("turbo:before-fetch-request", (event) => {
event.detail.fetchOptions.headers["X-Turbo-Nonce"] =
document.querySelector("meta[name='csp-nonce']")?.content
})
document.addEventListener("turbo:before-cache", () => {
document.querySelectorAll("script[nonce]").forEach((element) => {
element.setAttribute("nonce", element.nonce);
})
}) config.content_security_policy_nonce_generator =
lambda do |request|
request.env["HTTP_X_TURBO_NONCE"].presence ||
request.session.id.presence || SecureRandom.hex
end |
|
@terracatta's solution works and I have to add that in my case the third step is not necessary, because the restoration visits already contain all nonces and it shows no CSP violations. I'm using Symfony 7 together with Symfony UX Turbo. |
This issue is related to PR #291 I opened. The solution in the PR does not work properly when a page with the script is loaded for the first from the cache and then replaced with a fresh copy from the server. The first return to the page will work for the preview, as the right CSP nonce will be loaded, but it will fail for the fresh response from the server.
I was able to find out that the browser (I tried the latest Safari, Brave and Firefox) does not accept any changes to the
csp-noncemeta tag and will always want the first nonce, that was loaded on the first full page load. Any page visit made by Turbo will try to change the CSP nonce (duringPageRenderer#mergeHead), but that will be ignored by the browser.As it seems, the only solution would be to save the CSP nonce internally when Turbo will be loaded for the first time on the full page load and use it during all visits until another full page load.
Here is a log from the console, where you can see, that the browser want's always the first nonce.
Would it be a good solution? If yes, could someone point me to a place, where it should be done? I will gladly try to help on this and finish the PR properly 😅
Thank you.
The text was updated successfully, but these errors were encountered: