next.js icon indicating copy to clipboard operation
next.js copied to clipboard

CSP nonce being added to inline scripts on `next dev` but not on `next build & next start`

Open nirh1989 opened this issue 2 years ago • 12 comments

Verify canary release

  • [X] I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.1.0: Mon Oct  9 21:27:24 PDT 2023; root:xnu-10002.41.9~6/RELEASE_ARM64_T6000
Binaries:
  Node: 20.8.1
  npm: 10.2.4
  Yarn: 1.22.19
  pnpm: N/A
Relevant Packages:
  next: 14.0.4-canary.25
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.2.2
Next.js Config:
  output: N/A

Which example does this report relate to?

https://nextjs.org/docs/pages/building-your-application/configuring/content-security-policy

What browser are you using? (if relevant)

Chrome Version 119.0.6045.159 (Official Build) (arm64)

How are you deploying your application? (if relevant)

next build & next start

Describe the Bug

Hi, I Implemented the middleware exactly as mentioned in the documentation (few changes in headers according to my company). Buy running the app with next dev I can see that all inline scripts on my page got the attribute nonce with a value. However, the same does not happen when i run next build & next start, i know in this case the nonce attribute should be shown without any value (chrome hides it), but in my case the nonce attribute not appear at all.

run the app with next dev command.

<head>
        <meta charSet="utf-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1"/>
        <link rel="stylesheet" href="/mpa/_next/static/css/app/layout.css?v=1701242783101" data-precedence="next_static/css/app/layout.css"/>
        <link rel="preload" as="script" fetchPriority="low" nonce="NzMyNjMyZmYtOTQ1MC00NDE1LThkN2UtMTcwNTAxZTY3NGU0" href="/mpa/_next/static/chunks/webpack.js?v=1701242783101"/>
        <script src="/mpa/_next/static/chunks/main-app.js?v=1701242783101" async="" nonce="NzMyNjMyZmYtOTQ1MC00NDE1LThkN2UtMTcwNTAxZTY3NGU0"></script>
        <script src="/mpa/_next/static/chunks/app-pages-internals.js" async="" nonce="NzMyNjMyZmYtOTQ1MC00NDE1LThkN2UtMTcwNTAxZTY3NGU0"></script>
        <script src="/mpa/_next/static/chunks/app/page.js" async="" nonce="NzMyNjMyZmYtOTQ1MC00NDE1LThkN2UtMTcwNTAxZTY3NGU0"></script>
        <script src="/mpa/_next/static/chunks/app/layout.js" async="" nonce="NzMyNjMyZmYtOTQ1MC00NDE1LThkN2UtMTcwNTAxZTY3NGU0"></script>
        <title>HCU</title>
        <link rel="icon" href="/mpa/favicon.ico" type="image/x-icon" sizes="192x192"/>
        <script src="/mpa/_next/static/chunks/polyfills.js" noModule="" nonce="NzMyNjMyZmYtOTQ1MC00NDE1LThkN2UtMTcwNTAxZTY3NGU0"></script>
    </head>

run the app with next build and next start

<head>
        <meta charSet="utf-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1"/>
        <link rel="stylesheet" href="/mpa/_next/static/css/44d83526643d7f06.css" crossorigin="" data-precedence="next"/>
        <link rel="preload" as="script" fetchPriority="low" href="/mpa/_next/static/chunks/webpack-3d59e6430b82682b.js" crossorigin=""/>
        <script src="/mpa/_next/static/chunks/fd9d1056-18b181602163d226.js" async="" crossorigin=""></script>
        <script src="/mpa/_next/static/chunks/472-2cfd35dc71ab9d5b.js" async="" crossorigin=""></script>
        <script src="/mpa/_next/static/chunks/main-app-703e8bd9ded479e2.js" async="" crossorigin=""></script>
        <script src="/mpa/_next/static/chunks/0db0ade4-d281d1d1c1dddaf3.js" async=""></script>
        <script src="/mpa/_next/static/chunks/753-728b6b2b2c57e761.js" async=""></script>
        <script src="/mpa/_next/static/chunks/app/page-02e6a714554046f0.js" async=""></script>
        <script src="/mpa/_next/static/chunks/app/layout-86ba9729fc0dee41.js" async=""></script>
        <title>HCU</title>
        <link rel="icon" href="/mpa/favicon.ico" type="image/x-icon" sizes="192x192"/>
        <script src="/mpa/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js" crossorigin="" noModule=""></script>
    </head>

this cause for csp errors: 2023-11-29_09-48-52 (3)

Expected Behavior

next build & next start should have a nonce attribute inside each script tag, the same as when running next dev.

To Reproduce

i can't share my code due to company policy.

create new project, run it locally on your machine with next dev, a nonce attribute will show inside each script tag.

now run next build and next start, the none attribute not exist.

nirh1989 avatar Nov 29 '23 07:11 nirh1989

@nirh1989 as per the docs the nonce is only added if the pages are rendered dynamically.
You can try adding this to `page.tsx:

export const dynamic = 'force-dynamic'

this worked for me using the app router.
Hope this helps

Michael-Grupp avatar Dec 03 '23 16:12 Michael-Grupp

@Michael-Grupp thank you for the suggestion do i have to add this to all my page.tsx pages or just to root page.tsx or the layout.tsx? should i do anything with the dynamic variable except for that?

nirh1989 avatar Dec 05 '23 07:12 nirh1989

@nirh1989 I placed it on all my page.tsx. But as per the docs Route Segment Config you can place it in layout.tsx, page.tsx and route.ts. As per my understanding it is only to configure the behavior of the page.tsx, page.tsx or route.ts and you only use it to define the behavior.

Michael-Grupp avatar Dec 05 '23 09:12 Michael-Grupp

Thank you @Michael-Grupp i tried to add export const dynamic = 'force-dynamic' as you suggested to my page.tsx but it didn't help, i have the same result as before

nirh1989 avatar Dec 11 '23 09:12 nirh1989

@nirh1989 if you can't share you code you could start a sharable playground project starting form with strict csp

Michael-Grupp avatar Dec 12 '23 20:12 Michael-Grupp

Hi @Michael-Grupp in the example https://github.com/vercel/next.js/blob/canary/examples/with-strict-csp/app/page.js i can see they add to page.tsx this code

const nonce = headers().get('x-nonce')

  return <Script src="https://..." strategy="afterInteractive" nonce={nonce} />

i can't use it since my pages have "use client" maybe the setup should be different when using "use client"?

nirh1989 avatar Dec 13 '23 06:12 nirh1989

@nirh1989 the code snippet you mentioned, is an example how you could get the nonce in an component and add it to a Script component.
Try to avoid using 'use client' on the page.tsx level. Move the part that need 'use client' in a separate component.

Michael-Grupp avatar Dec 13 '23 09:12 Michael-Grupp

@Michael-Grupp my entire app has use client because all pages must have it, the app will not work without it due to other client components being used in my app, when using client components, you must add use client to your page that uses those components, so i can't remove the use client

nirh1989 avatar Dec 13 '23 11:12 nirh1989

@nirh1989 i understand the concept of use client. As per my understanding the implemented CSP concept needs server components. So a pure client component will never get a nonce, because it is generated in the middleware. Or am I wrong? So you want to wrap you client component into a server component as I was trying to explain. Docs: Moving Client Components Down the Tree. Hope this helps

Michael-Grupp avatar Dec 13 '23 11:12 Michael-Grupp

Encountering the same issue with the same parameters here. On all the other issues containing the same parameters, it looks like they just keep responding that the documentation has been updated, but the thing is, that new example doesn't really work -- (I have copy pasted it the same exact way) but just like you have described it does not work for a production build.

However like @Michael-Grupp commented adding force-dynamic to my layout.tsx did clear the problem, however does this mean that the CSP example will only work for server-rendered pages and will never work for static pages? Or is there a workaround for this?

margauxflores avatar Feb 27 '24 02:02 margauxflores

I was having exactly the same issue. Adding force-dynamic to layout.tsx fixed it.

It would be great if the example also covered use client apps. :+1:

quietbits avatar Mar 20 '24 22:03 quietbits

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

As a suggestion, Next.js should at least add integrity attributes to the

JarFrank avatar Oct 25 '24 22:10 JarFrank

From @JarFrank :

As a suggestion, Next.js should at least add integrity attributes to the

I think this is a good suggestion. If a page is generated statically, then Next should be able to inject the hash onto the script instead of the nonce. Then it could provide the list of injected hashes to the middleware. This would at least allow developers to work from the static base to inject hashes for their own content. (allow-list/hash style, since nonces can't work)

SlexAxton avatar Jan 13 '25 19:01 SlexAxton