netlify / plugin-csp-nonce

Build plugin to use a nonce for the script-src directive of your site's Content Security Policy.
https://csp-nonce.netlify.app
4 stars 4 forks source link

OWASP says nonce solution shouldn't be middleware #61

Open s6mike opened 10 months ago

s6mike commented 10 months ago

This is a very handy solution, but I read this while learning about CSPs:

"Don't create a middleware that replaces all script tags with "script nonce=..." because attacker-injected scripts will then get the nonces as well. You need an actual HTML templating engine to use nonces." (from https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html)

My understanding is that this package uses edge functions as a form of middleware to add the nonces, so presumably is open to this vulnerability.

Do OWASP's concerns apply in this case? If not, can you explain why not?

Does netlify have any alternative solutions which might mitigate the above issue? e.g. one which uses hashes?

Thanks for your attention!

jasonbarry commented 10 months ago

This is a great question, thanks for raising @s6mike! This would be a good to include as an FAQ in the readme.

It sounds like the vulnerability that OWASP is referring to here relates to cache poisoning, the practice of tricking the CDN to cache an additional malicious script in otherwise normal and safe text/html responses.

This plugin would prevent vulnerabilities from clientside XSS injections, but not from cache poisoning. If a response is served from our CDN (static files, ODB responses, etc) or from functions (SSR/ISR), this edge function sits in front of that, so it will transform the script tag of the offending script to include the nonce. Having said that, the attacker would need to find some other vulnerability to poison the cache in the first place -- either by exploiting a flaw in our platform, or a flaw in user code.

On the other hand, when an API response is fetched purely client-side and contains malicious code (for example, someone sends someone else a message that contains unescaped <script> tags, and the client renders that into the DOM), this plugin will block it from running. UI frameworks like React already have XSS protections built into render functions, but there are still escape hatches like __dangerouslySetInnerHTML that are vulnerable to exploits. This CSP plugin would prevent these types of attacks from executing.

To answer your question about hashes -- it would act the same as the nonce. The plugin could've be written to calculate the hash of each script, but that's more difficult with external resources since they need to be fetched in order to be calculated. Hashes would be susceptible to cache poisoning in the same way that nonces would.

TL;DR: this plugin isn't a silver bullet, but it's better than not having a CSP at all because it helps prevent from client-side XSS attacks.

s6mike commented 10 months ago

Thanks for comprehensive (and quick) reply, much appreciated!

helmax-y commented 3 weeks ago

@jasonbarry let me please clarify regarding client-side XSS

In readme of plugin it is stated that it follows strict-dynamic policy. According to this policy trusted scripts can append other scripts to the document, and they will be considered safe and trusted as well

So we can imagine the following scenario: browser downloads HTML, which has some legal script -> JS fetches JSON with comments, one of which is malicious -> comment content is unsafely embedded into the page, and some <script> gets into HTML -> since script was created by original trusted script, browser runs malicious JS

And the end we have CSP setup which didn't prevent even client-side XSS attack. Please correct me if I'm wrong

helmax-y commented 3 weeks ago

ok, now I see, that strict-dynamic will only allow scripts created with createElement() and probably block those inserted with innerHTML method 👍