Resources
Content-Security-Policy: default-src directive
Content-Security-Policy: style-src directive
Background
Inspect the codebase for all current usages, and based on the findings, either adjust the value of style-src
accordingly or update the codebase to a more secure implementation, then update the value of style-src
.
Style usage in the project
Inline style property
...
<div
className={background.className}
style={{
backgroundImage: `url(${background.url})`,
}}
/>
...
Unsafe inline styles
...
return (
<>
<Head />
{isiOS && (
<NextHead>
<style>
{`
html,
body {
height: 100vh;
}
`}
</style>
</NextHead>
)}
...
</>
...
Styles that are applied in JavaScript by setting the style
attribute directly or by using cssText
For example:
document.querySelector("div").setAttribute("style", "display:none;");
document.querySelector("div").style.cssText = "display:none;";
NOTE: our project does not have this
Style properties that are set directly on an element’s style
property.
const Drawer = ({ open, onClick }) => {
useEffect(() => {
document.body.style.position = open ? 'fixed' : 'static';
document.body.style.width = open ? '100%' : 'auto';
document.body.style.overflow = open ? 'hidden' : 'auto';
}, [open]);
...
Step-by-step guide
Set the Content Security Policy (CSP) header to include:
style-src 'self' 'nonce-${nonce}';
Dynamically generate a nonce
and set it in the x-nonce
response header. For example, in Next.js middleware:
...
export function middleware(request) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
const cspHeader = `
default-src 'self';
style-src 'self' 'nonce-${nonce}';
`;
// Replace newline characters and spaces
const contentSecurityPolicyHeaderValue = cspHeader
.replace(/\s{2,}/g, ' ')
.trim();
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-nonce', nonce);
requestHeaders.set(
'Content-Security-Policy',
contentSecurityPolicyHeaderValue
);
const response = NextResponse.next({
request: {
headers: requestHeaders,
},
});
response.headers.set(
'Content-Security-Policy',
contentSecurityPolicyHeaderValue
);
return response;
}
In the code that uses unsafe inline styles, retrieve the nonce value from the x-nonce
request header and set it as the nonce attribute on the <style>
element.
For example, the unsafe inline styles:
...
return (
<>
<Head />
{isiOS && (
<NextHead>
<style nonce={nonce}>
{`
html,
body {
height: 100vh;
}
`}
</style>
</NextHead>
)}
...
</>
...
However, according to 'nonce-'
If a directive contains a nonce and unsafe-inline, then the browser ignores unsafe-inline.
Since our project heavily relies on inline style properties, we need to refactor the code to use class names in order to resolve this error.
However, many of these inline styles depend on JavaScript variables at runtime, making it impractical to convert them to class names. For example:
<div
data-testid='detail-meta'
className='detail-meta'
style={{
minHeight: isArtist ? '14vw' : 'auto',
}}>
...
</div>
Therefore, we set the style-src
directive to 'self' 'unsafe-inline'
instead of using a nonce
to resolve this issue.
Further thoughts
What happens if we remove default-src
and do not add any fetch directives?
For example, if we remove default-src
along with style-src
, style-src-elem
, and style-src-attr
from the Content-Security-Policy
headers.
Removing default-src
and using wildcards for other directives fails to resolve the Content Security Policy (CSP) Header Not Set error and the complex style-src
issues. In fact, it causes additional problems:
- CSP: Failure to Define Directive with No Fallback
- CSP: Wildcard Directive
- CSP: style-src unsafe-inline
- Content Security Policy (CSP) Header Not Set
What happens when the directive values 'unsafe-inline'
and 'nonce-${nonce}'
are set together?
Get the error:
Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self' 'unsafe-inline' 'nonce-MjNjZTAzNDEtYWMxMC00OGViLTg5NDYtZjMxOTg0ODg2MjVm'". Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list.
As shown in this image:
To resolve this issue, remove either 'unsafe-inline'
or 'nonce-${nonce}'
from the directive.
Top comments (0)