Skip to content

CSP nonce not being added to inline scripts #55638

Closed
@sleepdotexe

Description

@sleepdotexe

EDIT: See this comment below for an important update.


Verify canary release

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

Provide environment information

Operating System:
      Platform: win32
      Arch: x64
      Version: Windows 10 Home
    Binaries:
      Node: 18.17.0
      npm: N/A
      Yarn: N/A
      pnpm: N/A
    Relevant Packages:
      next: 13.5.1-canary.1
      eslint-config-next: N/A
      react: 18.2.0
      react-dom: 18.2.0
      typescript: N/A
    Next.js Config:
      output: N/A

Which example does this report relate to?

with-strict-csp

What browser are you using? (if relevant)

Chrome 116.0.5845.188 (Official Build) (64-bit)

How are you deploying your application? (if relevant)

Vercel, Nodejs 18

Describe the Bug

According to the docs and example, the correct way to implement a strict CSP with nonces is by using middleware. However, even with a clean install of the example, the nonce is not being added to scripts correctly.

The nonce property is appearing on <script> tags, however the value is always empty.

<body>
  <!-- ... -->
  <script src="/_next/static/chunks/webpack.js?v=1695191539001" nonce="" async=""></script>
  <script nonce="">(self.__next_f=self.__next_f||[]).push([0])</script>
  <!-- etc. -->
</body>

A custom next/script tag also does not have the nonce value added.

export default function RootLayout({ children }) {
  const nonce = headers().get("x-nonce");
  console.log(nonce);

  return (
    <html lang="en">
      <body>{children}</body>
      <Script
        src={`https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX`}
        strategy="afterInteractive"
        nonce={nonce}
      />
    </html>
  );
}

gives us:

<script src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX" nonce="" data-nscript="afterInteractive"></script>

If I try to show the nonce value on the page itself, it will display valid nonce value, so I don't think the error is with the middleware/nonce creation.

// app/page.js
import { headers } from "next/headers";

export default function Page() {
  const nonce = headers().get("x-nonce");
  console.log(nonce);

  return (
    <p>nonce: {nonce}</p>
  );
}

gives us:

<p>nonce: <!-- -->MzgyYzQ2OGQtMWRhYS00OWFjLTk5NWUtYzliOTM4YjI4NmMx</p>

Our CSP response header is also correctly showing the nonce:
default-src 'self'; script-src 'self' 'nonce-MzgyYzQ2OGQtMWRhYS00OWFjLTk5NWUtYzliOTM4YjI4NmMx' 'strict-dynamic' 'unsafe-eval';

Expected Behavior

Next's inline <script> tags (including ones created from <Script> components) should have the nonce value added.

// middleware.js
import { NextResponse } from 'next/server'

export function middleware(request) {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64') 
  //nonce = MzgyYzQ2OGQtMWRhYS00OWFjLTk5NWUtYzliOTM4YjI4NmMx

  //...

  requestHeaders.set('x-nonce', nonce);
  requestHeaders.set(
    'Content-Security-Policy',
    // Replace newline characters and spaces
    cspHeader.replace(/\s{2,}/g, ' ').trim()
  );

  return NextResponse.next({
    headers: requestHeaders,
    request: {
      headers: requestHeaders,
    },
  })
}
// app/layout.js
import { headers } from "next/headers";
import Script from "next/script";

export default function RootLayout({ children }) {
  const nonce = headers().get("x-nonce");
  console.log(nonce);

  return (
    <html lang="en">
      <body>{children}</body>
      <Script
        src={`https://www.googletagmanager.com/gtag/js?id=G-AAAAAAAAAA`}
        strategy="afterInteractive"
        nonce={nonce}
      />
    </html>
  );
}

Should output:

<html lang="en">
    <head><!-- ... --></head>
    <body>
        <!-- ... -->
        <script src="/_next/static/chunks/webpack.js?v=1695191539001" nonce="MzgyYzQ2OGQtMWRhYS00OWFjLTk5NWUtYzliOTM4YjI4NmMx" async=""></script>
        <!-- ... -->
        <script src="https://www.googletagmanager.com/gtag/js?id=G-AAAAAAAAAA" nonce="MzgyYzQ2OGQtMWRhYS00OWFjLTk5NWUtYzliOTM4YjI4NmMx" data-nscript="afterInteractive"></script>
    </body>
</html>

To Reproduce

  • Install the example (with a command such as npx create-next-app --example with-strict-csp with-strict-csp-app)
  • Run the example (npm run dev)
  • Look at the HTML output in console and check <script> tags for the nonce value.

Metadata

Metadata

Assignees

No one assigned

    Labels

    examplesIssue was opened via the examples template.locked

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions