vercel / next.js

The React Framework
https://nextjs.org
MIT License
127.33k stars 27.02k forks source link

[App Router] Content Security Policy Broken #63015

Open moloch-- opened 8 months ago

moloch-- commented 8 months ago

Link to the code that reproduces this issue

https://github.com/moloch--/nextjs-broken-csp

To Reproduce

  1. Follow directions at https://nextjs.org/docs/app/building-your-application/configuring/content-security-policy
  2. Add unsafe-eval to CSP as it's required, though the documentation doesn't include it
  3. npm run build and npm run start
  4. Page fails to load, the CSP response headers are present but the nonce is not rendered into the DOM

Notably, the nonces are rendered in the DOM when using npm run dev but only fail to render properly in production builds.

Current vs. Expected behavior

Current

Content-security-policy requires unsafe-eval contrary to the offically documented examples, and nonces are not rendered in production builds.

Expected

Content-security-policy can be used without any unsafe content sources, and works with production builds.

Provide environment information

Next.js v14.1.1
Node v20.x - v21.x
Docker `public.ecr.aws/docker/library/node:20-slim`

Which area(s) are affected? (Select all that apply)

App Router

Which stage(s) are affected? (Select all that apply)

next start (local)

Additional context

CSP without unsafe-eval

Screenshot 2024-03-07 at 11 56 16 AM

Docs' Example CSP in Production Build (Nonces)

Screenshot 2024-03-07 at 11 57 30 AM
AlexBueckig commented 8 months ago

Can confirm. 14.0.4 still works fine for me.

coffeecupjapan commented 8 months ago

@moloch-- I am not quite sure, since I have not used CSP in my hobby or in work coding, but document says you must use dynamic rendering to add nonces. , and I think app-router is default to cache. So I think you have to add

export const dynamic = "force-dynamic"

or something to prevent next.js from cacheing in page.tsx in your reproduction codes.

In my case, adding above code fix console.log to warn.

moloch-- commented 8 months ago

Doesn't seem like I should have to force dynamic rendering for every page just to use CSP, so still seems like a bug?

@AlexBueckig would you be able to link to a working example?

AlexBueckig commented 8 months ago

Adding force-dynamic solved the problem for me. I totally makes sense, now that I'm thinking about it. A new nonce gets created for every request by your middleware function. If you cache a rendered page the nonces in this page are a value from a previous request. But since you create a new csp header for every request, there is a mismatch with these nonces and csp breaks.

For now this seems to be the only option if you want to use nonces.

I don't know your project, but if you aren't using external scripts e.g. tracking or whatever you can just set static csp headers without the need for strict-dynamic and nonces. These versions should be cacheable since they are not dependant on a random generated value.

AlexBueckig commented 8 months ago

Experienced this issue again, back on 14.1.3. force-dynamic is activated and no other CSP-Errors occur.

Here are some screenshots from the developer tools showing the error and file:

Bildschirmfoto vom 2024-03-11 12-02-04 Bildschirmfoto vom 2024-03-11 12-05-49

moloch-- commented 8 months ago

btw, adding export const dynamic = "force-dynamic" doesn't seem to have any affect for me :-/

moloch-- commented 8 months ago

Okay for anyone else that sees this, I figured it out. I had "use client" in my layout.tsx which means the nonces are never inserted even if you use force-dynamic

backdevjung commented 1 month ago

"unsafe-eval, unsafe-inline" as a csp header just doesn't look right. It's basically a false sense of security.

The problem is coming from the inline script tags, which is generated by Next.js framework on whenever I build project and run it (as opposed to running dev mode). Since I cannot add nonce to a code that I myself did not write, I tried hash method. I found all the hash of the inline scripts. Yet it still fails because everytime I rebuild, there's one more inline script with a new hash value.

JarFrank commented 1 month ago

@backdevjung I’m experiencing the same issue: Next.js is generating multiple Githubissues.

  • Githubissues is a development platform for aggregating issues.