Content-Security-Policy cannot be used with secure instructions
See original GitHub issueđ Bug report
The Content-Security-Policy
header allows you to give browsers directives on how to handle stuff securely for your site.
For example, you can restrict all scripts to be only executed if theyâre directly hosted on your site.
With this, a script on cdnjs.com wouldnât be executed by the browser. No other host than your own site host would be allowed.
All this severely restricts the attack surface of your site.
More info on this:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
- https://content-security-policy.com/
Bug Itâs actually impossible to set secure policies using Chakra because of the use of inline scripts and inline styles.
The only inline script I found is the <ColorModeScript>
. A workaround is easy to implement in this case, all you need to do is add a props to the component on which we can pass the secure nonce
token. I provide the modified code below.
The bigger problem is about EmotionJs. It litters the DOM with inline styles. While the severity of inline styles attacks are less than inline scripts, itâs still a vulnerability. Unfortunately, we donât have access to the underlying structure of EmotionJs, so we personally canât add a nonce
ourselves.
đ„ Steps to reproduce
Add the Content-Security-Policy
header on a website with this value:
"base-uri; child-src; connect-src 'self' *; default-src; font-src; form-action; frame-ancestors 'self' *; frame-src 'self'; img-src 'self' * data:; manifest-src 'self'; media-src 'self'; object-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline';"
Make sure your site use the <ColorModeScript>
component to test the result in Dev Tools.
đ» Link to reproduction
Not really possible to show this in CodeSandbox
đ§ Expected behavior
A nonce
we provided being added to inline script, which will allow the script to be executed and not be blocked by the browser.
A nonce
we provided being added to inline style, which will allow the styles to be applied and not be blocked by the browser.
đ§ Possible Solution
For EmotionJs, itâs supposedly a matter of passing the nonce
to a cache. See https://emotion.sh/docs/@emotion/cache#nonce
For ColorModeScript
, here is the code as-is from Chakra (1.2.1) that add the nonce
. Itâs literally 2 small changes.
import * as React from "react"
import { ColorMode } from "./color-mode-provider"
type Mode = ColorMode | "system" | undefined
function setScript(initialValue: Mode) {
const mql = window.matchMedia("(prefers-color-scheme: dark)")
const systemPreference = mql.matches ? "dark" : "light"
let persistedPreference: Mode
try {
persistedPreference = localStorage.getItem("chakra-ui-color-mode") as Mode
} catch (error) {
console.log(
"Chakra UI: localStorage is not available. Color mode persistence might not work as expected",
)
}
const isInStorage = typeof persistedPreference === "string"
let colorMode: Mode
if (isInStorage) {
colorMode = persistedPreference
} else {
colorMode = initialValue === "system" ? systemPreference : initialValue
}
if (colorMode) {
const root = document.documentElement
root.style.setProperty("--chakra-ui-color-mode", colorMode)
}
}
interface ColorModeScriptProps {
initialColorMode?: Mode
nonce?: string
}
/**
* Script to add to the root of your application when using localStorage,
* to help prevent flash of color mode that can happen during page load.
*/
export const ColorModeScript = (props: ColorModeScriptProps) => {
const { initialColorMode = "light" } = props
const html = `(${String(setScript)})('${initialColorMode}')`
return <script nonce={props.nonce} dangerouslySetInnerHTML={{ __html: html }} />
}
đ System information
Software | Version(s) |
---|---|
Chakra UI | 1.2.1 |
Browser | Chrome |
Operating System | Windows |
đ Additional information
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:7 (2 by maintainers)
@mattiasw Youâre right, using SSG with NextJs will not create a new
nonce
on every request. Itâs correct to say I would need to use a hash at this point and itâs a painâŠIdeally, the script shouldnât be inline to begin with. I extracted it into a new file in my
public/
folder at the moment. Nononce
orhash
required. But this ainât the easiest thing to maintainâŠReading different threads over at NextJs Github, it looks like theyâre working on something that would allow to generate a
nonce
for SSG pages. So at some point thenonce
should work on every request and be the way to go.While waiting for a more permanent solution, I would much prefer to reduce as much as possible the attack surface of my site. While far from being ideal and wouldnât be 100% secure, a fixed
nonce
would certainly be a thousand times more secure than a blanketunsafe-inline
. At least youâd have to be specifically targeted to be breached, unlike withunsafe-inline
âŠAnd without
unsafe-inline
, I canât get my site running with Chakra, mostly because of styling, but also because of the ColorModeScript. Probably gonna end up hardcoding a fixednonce
to reduce exposure. Fun times !!! đ@TimKolberger I had a bit of trouble making it work, the console errors arenât very explicit as to which styles are problematic exactlyâŠ
Once I fixed the NextJs noise (Next/Image & 2 inline style tags), I was able to make Emotion work with the cache.
Although, I had to reverse the Emotion provider with the Chakra provider because
<ChakraProvider>
include a css reset functionality and another style tag I donât remember for what.Thus the exact solution was this:
For those using js, itâs practically the same, just use
.js
as extension instead