You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Inline scripts generated as a result of appDir preventing use of strict CSP #43743
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 21.6.0: Wed Aug 10 14:28:23 PDT 2022; root:xnu-8020.141.5~2/RELEASE_ARM64_T6000
Binaries:
Node: 16.16.0
npm: 8.11.0
Yarn: N/A
pnpm: N/A
Relevant packages:
next: 13.0.3
eslint-config-next: 13.0.6
react: 18.2.0
react-dom: 18.2.0
Which area(s) of Next.js are affected? (leave empty if unsure)
App directory (appDir: true)
Link to reproduction - Issues with a link to complete (but minimal) reproduction code will be addressed faster
To Reproduce
Just setup a basic "Hello world" app using the appDir feature, with: npx create-next-app@latest --experimental-app.
Describe the Bug
I posted a Help discussion about this yesterday (#43710) but it was suggested that I create an issue for it.
I've noticed a number of inline scripts being injected into the page, all beginning self.__next_f... They seem to contain parts of random stringified components / webpack chunks. They're only generated when using the appDir. Obviously this isn't ideal from a CSP standpoint, as they're just generic inline scripts (so no src to whitelist, unlike the usual /_next/static/ scripts).
Is there a plan for these scripts to be removed in future versions, or is there a recommended way of maintaining a strict CSP (i.e. without using unsafe-inline)? Perhaps using nonces, hashes or similar?
Thanks
Expected Behavior
No inline scripts generated as a result of using the appDir feature.
Which browser are you using? (if relevant)
No response
How are you deploying your application? (if relevant)
When Generating static pages, the export worker uses a MockRequest that does not contain the headers. So the server doesn't retrieve the csp header here and the page is generated without the nonce value.
Could it be possible somehow to generate this request with knowledge of the next config ? Meanwhile, moving to 'unsafe-inline'
Right now in Next@canary, if you add a content-security-policy with a nonce-xxx value inside the script-src directive (via middleware), it will pass that nonce value to the React rendering engine. However, React is only including nonce in inline scripts, not <script src=...> scripts.
@danieltott I think that bug is valid, but either tangential to this one or else incomplete. This is related to inline scripts, which do properly get the nonce value, like you stated, but only during development. When running a production-like build with next build && next start the nonce-value is also omitted from the inline scripts, causing this issue. (This is confirmed as recently as 13.4.1)
We are able to work around this on the pages router because the _document.tsx file can use NextScript.getInlineScriptSource(props) to get the inline-script source, which we can then generate a hash for and include as a safe sha. However, this is another thing which is either unintentionally omitted or intentionally locked down in the new app router paradigm.
The PR in React (react/react#26738) has been merged, and as soon as there's a new release, NextJS (via React) will render nonce attributes in all bootstrap scripts.
exportasyncfunctionmiddleware(request: NextRequest){// generate CSP and nonceconst{ csp, nonce }=generateCsp();// Clone the request headersconstrequestHeaders=newHeaders(request.headers);// set nonce request header to read in pages if neededrequestHeaders.set('x-nonce',nonce);// set CSP header// switching for report-only or regular for repro on// https://github.com/vercel/next.js/issues/48966constheaderKey=request.nextUrl.pathname==='/csp-report-only'
? 'content-security-policy-report-only'
: 'content-security-policy';// Set the CSP header so that `app-render` can read it and generate tags with the noncerequestHeaders.set(headerKey,csp);// create new responseconstresponse=NextResponse.next({request: {// New request headersheaders: requestHeaders,},});// Also set the CSP so that it is outputted to the browserresponse.headers.set(headerKey,csp);returnresponse;}
The tricky part (and this may be a bug and/or new feature to fix/change, or it may be as designed) is you need to set the header twice, once as part of the request while creating the NextResponse and then again as a response header. app-render needs the header to be there in the request so that it can pull out the nonce.
@zackdotcomputer OK actually - I think this issue is closeable once nextjs 13.4.2 is released! I just did some more digging, and it looks like next.jsis now using the React canary channel for react-builtin which is what Next uses for rendering.
I assume that Next using React canary is only for Next's own canary builds, so this won't get released until React 18.3.0 is officially out.
You can actually see this working on my repro on vercel: https://nextjs-csp-report-only.vercel.app/csp - the nonce value is present for all scripts and there are no warnings at all in the console!
OK sorry for all the comments - but it looks like that upgrade to the react 18.3 canary landed in nextjs 13.4.0 - so it's good to go! I believe this issue can be safely closed.
React canary is used when you enable the Server Actions flag. I'll close this issue based on @danieltott's comments. If you're still running into it let us know.
@timneutkens Just as a clarification, I don't think you need to enable server actions. I'm pretty sure React canary is used by default in the nextjs server code (the compiled react-builtin), and when any of the experimental flags are on, next uses the latest experimental release from react. See here:
Is there a solution for statically-generated pages that doesn't require the use of middleware? E.g., calculate sha sums at build time, and inject them into the csp header in the next.config.js?
@paulsouche@igordanchenko CSP nonces are incompatible with static pages, since they must be different for each request. My example was deopted on purpose for this reason (by reading next/headers).
For statically generated pages, CSP Hashes are the only solution, and there doesn't exist a built-in path forward for that in Next.js.
I'm not a maintainer here, but my guess would be the best way forward would be a new discussion - I think having next.js handle this might end up getting a little complicated, but I could imagine a custom middleware-based solution here...
If you do end up creating a new discussion, I'd appreciate a mention (or an update here) so I can follow along!
This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.
Activity
bjarn commentedon Mar 21, 2023
This issue still persists in the latest 13.2.4.
pepijn-vanvlaanderen commentedon Mar 22, 2023
+1
lsagetlethias commentedon Apr 11, 2023
This issue still persists in the latest 13.3.0.
Potentially critical.
paulsouche commentedon Apr 13, 2023
Hello !
When Generating static pages, the export worker uses a MockRequest that does not contain the headers. So the server doesn't retrieve the csp header here and the page is generated without the nonce value.
Could it be possible somehow to generate this request with knowledge of the next config ? Meanwhile, moving to 'unsafe-inline'
Thanks anyway for the great job
danieltott commentedon Apr 28, 2023
This is actually a bug in React.
Right now in Next@canary, if you add a
content-security-policywith anonce-xxxvalue inside thescript-srcdirective (via middleware), it will pass that nonce value to the React rendering engine. However, React is only includingnoncein inline scripts, not<script src=...>scripts.zackdotcomputer commentedon May 10, 2023
@danieltott I think that bug is valid, but either tangential to this one or else incomplete. This is related to inline scripts, which do properly get the
noncevalue, like you stated, but only during development. When running a production-like build withnext build && next startthe nonce-value is also omitted from the inline scripts, causing this issue. (This is confirmed as recently as 13.4.1)We are able to work around this on the pages router because the
_document.tsxfile can useNextScript.getInlineScriptSource(props)to get the inline-script source, which we can then generate a hash for and include as a safesha. However, this is another thing which is either unintentionally omitted or intentionally locked down in the new app router paradigm.danieltott commentedon May 10, 2023
@zackdotcomputer right now as of the latest canary (
13.4.2-canary.4) thenoncevalue is there in inline scripts in production builds.Check out the source here: https://nextjs-csp-report-only.vercel.app/csp
Repo: https://github.com/Sprokets/nextjs-csp-report-only
The PR in React (react/react#26738) has been merged, and as soon as there's a new release, NextJS (via React) will render
nonceattributes in all bootstrap scripts.danieltott commentedon May 10, 2023
Here's the middleware function (full source):
danieltott commentedon May 10, 2023
The tricky part (and this may be a bug and/or new feature to fix/change, or it may be as designed) is you need to set the header twice, once as part of the
requestwhile creating theNextResponseand then again as a response header.app-renderneeds the header to be there in therequestso that it can pull out the nonce.danieltott commentedon May 10, 2023
@zackdotcomputer OK actually - I think this issue is closeable once nextjs
13.4.2is released! I just did some more digging, and it looks likenext.jsis now using the React canary channel forreact-builtinwhich is what Next uses for rendering.I assume that Next using React canary is only for Next's own canary builds, so this won't get released until React
18.3.0is officially out.You can actually see this working on my repro on vercel: https://nextjs-csp-report-only.vercel.app/csp - the
noncevalue is present for all scripts and there are no warnings at all in the console!danieltott commentedon May 10, 2023
OK sorry for all the comments - but it looks like that upgrade to the react
18.3canary landed in nextjs13.4.0- so it's good to go! I believe this issue can be safely closed.timneutkens commentedon May 11, 2023
React canary is used when you enable the Server Actions flag. I'll close this issue based on @danieltott's comments. If you're still running into it let us know.
danieltott commentedon May 11, 2023
@timneutkens Just as a clarification, I don't think you need to enable server actions. I'm pretty sure React canary is used by default in the nextjs server code (the compiled
react-builtin), and when any of the experimental flags are on, next uses the latestexperimentalrelease from react. See here:next.js/package.json
Lines 200 to 205 in f169c32
timneutkens commentedon May 11, 2023
Sorry for the confusion: Without the server actions flag we use
react@canary, with the server actions flag we usereact@experimentalindeed!igordanchenko commentedon May 16, 2023
Is there a solution for statically-generated pages that doesn't require the use of middleware? E.g., calculate sha sums at build time, and inject them into the csp header in the next.config.js?
paulsouche commentedon May 22, 2023
@danieltott I agree with @igordanchenko, in your example, all your pages are deopted
Any chance it could work with statically-generated pages ? Do you want us to open a new issue ?
Thanks anyway for the great job
danieltott commentedon May 22, 2023
@paulsouche @igordanchenko CSP nonces are incompatible with static pages, since they must be different for each request. My example was deopted on purpose for this reason (by reading
next/headers).For statically generated pages, CSP Hashes are the only solution, and there doesn't exist a built-in path forward for that in Next.js.
I'm not a maintainer here, but my guess would be the best way forward would be a new discussion - I think having next.js handle this might end up getting a little complicated, but I could imagine a custom middleware-based solution here...
If you do end up creating a new discussion, I'd appreciate a mention (or an update here) so I can follow along!
github-actions commentedon Jun 22, 2023
This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.