Move CSP to nonces; remove unsafe-inline from script-src and style-src

unsafe-inline on script-src neutered CSP as an XSS defense on the login and OAuth
consent pages (the highest-value targets in an IdP). Switch to a per-response
nonce for both script-src and style-src and drop unsafe-inline entirely.

- Add a random per-response nonce generator and apply it to script-src/style-src.
- Remove :unsafe_inline from both directives.
- Nonce the one hand-written inline script (dark-mode detection in the layout).
- Convert the 2 static style="display:none" attributes to class="hidden" (their
  runtime toggle is done via element.style in JS, which CSP does not govern).

importmap-rails (2.2.3) already stamps the nonce onto its generated inline
importmap/module/preload tags, and Turbo (2.0.23) reads csp_meta_tag for its
injected <style>, so no other view changes were needed. Adds an integration test
asserting the enforcing header carries nonces, omits unsafe-inline, and that the
inline script's nonce matches the header.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Dan Milne
2026-06-11 20:42:28 +10:00
parent 44892e3301
commit d49e7ce4f5
5 changed files with 58 additions and 9 deletions

View File

@@ -38,7 +38,7 @@
</svg>
Continue with Passkey
</button>
<div data-webauthn-target="error" class="mt-2 text-sm text-red-600" style="display: none;"></div>
<div data-webauthn-target="error" class="mt-2 text-sm text-red-600 hidden"></div>
</div>
<!-- Password section - shown by default, hidden if WebAuthn is required -->