-
Notifications
You must be signed in to change notification settings - Fork 337
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
feat: meta based csp #228
feat: meta based csp #228
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"default-src": [ | ||
"'self'" | ||
], | ||
"script-src": [ | ||
"'self'", | ||
"https://rum.hlx.page/" | ||
], | ||
"connect-src": [ | ||
"'self'", | ||
"https://rum.hlx.page/" | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -72,11 +72,28 @@ async function loadEager(doc) { | |
} | ||
} | ||
|
||
/** | ||
* sets the Content-Security-Policy meta tag to the document based on JSON file | ||
*/ | ||
|
||
async function setCSP() { | ||
const resp = await fetch(`${window.hlx.codeBasePath}/scripts/csp.json`); | ||
const json = await resp.json(); | ||
const directives = Object.keys(json); | ||
const policy = directives.map((directive) => `${directive} ${json[directive].join(' ')}`).join('; '); | ||
const meta = document.createElement('meta'); | ||
meta.setAttribute('http-equiv', 'Content-Security-Policy'); | ||
meta.setAttribute('content', policy); | ||
document.head.appendChild(meta); | ||
davidnuescheler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/** | ||
* Loads everything that doesn't need to be delayed. | ||
* @param {Element} doc The container element | ||
*/ | ||
async function loadLazy(doc) { | ||
await setCSP(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd check if this is effective in blocking eagerly loaded resources that violate the CSP. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i am not sure that i am really that worried about the CSPs protecting block code... i am somewhat doubtful of the allowlist based approach altogether, so maybe a nonce based There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Revisiting this, I think this is right, but only the first step. The next step should be @chicharr's lightweight tag management controlling the CSP There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think function setCSP() {
const config = {
"default-src": [
"'self'"
],
...
};
const directives = Object.keys(config);
const policy = directives.map((directive) => `${directive} ${config[directive].join(' ')}`).join('; ');
...
} |
||
|
||
const main = doc.querySelector('main'); | ||
await loadBlocks(main); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could store the value of
csp.json
directly inscripts.js
to avoid the additional request.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure… but still not sure why we'd do that in JS code instead of just using headers since this doesn't change much usually. It's kind of a one-time setup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely agree that doing it via headers would be more secure. The disadvantage with JS is clear, you could always have an XSS kick in before the meta tag is set.
The only advantage that I see of the JS code is that it can be out of the box in the boilerplate and new projects would need to adapt it, so it creates awareness, that there's something they should do. We could even put a recommendation that they should move it to headers.
Alternatively, not sure how it could be done, but all new projects (to not break existing ones) could start with an out of the box CSP header, that needs to be deleted or modified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm more from the "secure by default" camp, and I'd prefer having it clearly in the pre go-live checklist than letting everyone assume that the JS approach will properly protect them all the time. I think giving the community the impression that you can fully handle this client-side is setting us up for escalations down the road when someone starts exploiting the loophole. I'd also argue that this is likely an IT or sec team operation as part of the production hardening that could likely be managed at the CDN level directly by experts, and not something I'd leave up to authors or "script kiddies".
My proposal would be to do:
headers
worksheet in the GDrive for the boilerplateThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with the "experts" managing those at the CDN level is that they pile millions of rules, never clean it up and nobody knows what's in there. The header becomes so huge that it adds several KBs for every single request. We even had the case (several times) where it was so huge that request headers size was too large for our lambdas (more than 8KB!)...
Doing it client side would allow to cache the rules once and lighten all requests.
Why would it be less secure to do it client side ? Is this is a gut feeling or something backed with data ? If you are able to get rid of the rules if they are loaded client side, you can also get rid of the header...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like we are all in the same camp. I think we are simply debating what is the most efficient and effective way to make that happen, what is the best thing that we can put in place to make sure things don't remain just at documentation level.
I agree that the most secure way is to do it using http headers.
It is more secure, because the header sets the CSP before any JS is executed.
If you set it on the client side and attacker might manage to do their XSS before the code that sets the CSP gets a chance to execute. Taking the example of the current implementation where
setCSP
is called inloadLazy
an XSS could happen in an LCP block or something else running inloadEager
, or if a customer has a script in<head>
that does something beforescripts.js
.I agree there should be an entry in the pre go-live checklist/CDN configuration docs, but I feel like if during pre-golive it is the first time a CSP is discussed, it is too late. It means that all code has already been written and tested. Setting a CSP so late means everything needs to be retested to make sure all the dynamic scripts being loaded still work.
I was thinking of the client side as a way to:
I agree this could work as an early signal for devs that they should start working on their CSP early and built it out during development, but only works if people actually copy that worksheet, whereas not using the boilerplate is probably less likely to happen.
btw, the PSI report is already scanning and telling us all this.
As David above, I'm wondering if a
nonce
+strict-dynamic
wouldn't as an out of the box solution that works for majority of customers. https://content-security-policy.com/strict-dynamic/