Reputation: 1268
I am trying to setup the CSP header in my NextJS app (App router).
I added the "Content Security Policy" Security Header, specifically the "src-script" directive. BUT I am getting an error, basically saying that there is some unknown script host (besides the ones listed) that is violating this directive. Chrome isn't helpful in that it doesn't know WHICH script is violating this.
Problem
How do I identify all the src-script sources in my app, without having to pore through the codebase?
vercel.json snippet:
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; script-src 'self' *.posthog.com; style-src 'self';"
}
]
}
]
}
Error
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src" 'self' https://.posthog.com".*
Upvotes: 2
Views: 2350
Reputation: 8105
In addition to the above, if anyone is finding a conflicting warning after constructing CSPs with unsafe-eval on script-src triggered by a next/script due it using dangerouslySetInnerHTML, set the prop strategy="lazyOnload".
<Script
dangerouslySetInnerHTML={{ ...etc
strategy="lazyOnload"
Also, took me a while to figure i needed my middleware in src/middleware.ts, as i have my project with Payload and the FE dir structure looks like: src/app/(frontend)/
Upvotes: 0
Reputation: 1268
OK I figured it out. There were several issues. SUPER annoying that nextjs docs don't make these explicit.
My original attempt was to add CSP as part of vercel.json, but you should set it in middleware instead.
After following this doc and adding the CSP middleware, it wasn't clear how the damn nonce's were getting added to the _next script tags. Answer: They just automagically get added!! (in dev it just says <script nonce>
- very hard to tell if the nonce value is getting applied).
layout.tsx
to dynamic
.Thanks to this answer I was able to answer this one. Again, the NextJS docs on this are confusing. Yes, I know, it does explicitly say:
Every time a page is viewed, a fresh nonce should be generated. This means that you must use dynamic rendering to add nonces.
But as someone who's not so familiar with Nextjs, it would've been nice if the example spelled it out. Something like "to use dynamic rendering, add this to your root layout.tsx
file: export const dynamic = "force-dynamic"
"
If you're in dev, you need it to be unsafe-eval
otherwise it won't work locally. Here's my CSP string:
const cspHeader = `
default-src 'self';
connect-src 'self' some.domain.com another.domain.com *.example.com ${isDev ? `${process.env.NEXT_PUBLIC_API_URL}` : ''};
script-src 'self' 'nonce-${nonce}' 'strict-dynamic' ${isDev ? "'unsafe-eval'" : ''};
style-src 'self' 'unsafe-inline';
img-src 'self' blob: data: some.domain.com;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
`
Where NEXT_PUBLIC_API_URL
is localhost:8000 or whatever your backend URL is.
connect-src
directive, not script-src
. This took a lot of experimenting, but I got there. #3 above for examples.For example, if you're loading posthog into your app, it should be:
connect-src 'self' *.posthog.com;
Other helpful links: here
Upvotes: 10