Improper defaults in generated Content Security Policy

I think there’s a problem with the :https symbol in the CSP that Rails autogenerates for me. This symbol appears to allow scripts from every HTTPS-enabled server on the internet, even though I did not explicitly allowlist them. When I remove the :https symbol, the browser blocks these scripts as expected.

As a test, I created a Rails app with the autogenerated CSP enabled and added this script tag (it simply runs alert(1)):

<script src="https://portswigger-labs.net/A.JS"></script>

I expected the CSP to block this script from running, but it did not. It only stopped running after I changed the CSP from:

script_src :self, :https

to:

script_src :self

My understanding is that :https is effectively an additional allowed source, rather than a requirement that an otherwise-allowed source must use HTTPS. If that’s correct, I’m wondering whether :https should really be part of the generated default CSP, since it appears to undermine the protection provided by the allowlist.

This is particularly easy to miss because :self, :https looks like it could mean “allow my own scripts, provided they’re served over HTTPS,” when it actually means “allow my own scripts or scripts from anywhere over HTTPS.”

I think a better way to encourage HTTPS in the default CSP would be to use the upgrade_insecure_requests directive that Rails already supports. This would provide the HTTPS behavior without broadening the list of allowed script sources.

I don’t thinK the https is there to encourage https. I’d guess the primary motivation is to allow external CDNs to work. It makes sense in initial bootstrapping phase when you’d just want to pin an importmap pointing to somewhere and keep moving.

But I agree it’s not the most secure, and I couldn’t find it documented anywhere what :https means (but you’re right - I’ve seen the same behavior, it’s any ULI with https schema)

I hadn’t considered that :https could be useful for the bootstrapping phase; that makes sense. I still don’t think it’s a good fit for the default, though, since the generated CSP is commented out by default, and by the time the developer enables it the bootstrapping phase should be finished. My main concern remains that :self, :https looks like a secure default, which can easily lead you to enable the CSP while inadvertently weakening its protection.

I agree, and I think you’d have a decent chance at merging such PR. I asked Claude to research rationale, and its just what Twitter was using at that time. “Decent defaults around 2013”.

Definitely worth a revisit.

the source rationale is anti-mixed-content, from a gem whose defaults predate CSP2/nonces and were designed for a 2013-era world where HTTP-vs-HTTPS was the live threat and unsafe-inline was the XSS control you actually cared about. https: was the cheap way to say “no cleartext” before upgrade-insecure-requests and block-all-mixed-content existed.