Как установить директивы CSP в NextJs с «строгой динамикой» и одноразовым номером или хэшем

Я потратил несколько часов, чтобы найти решение этой проблемы. Я думал, что если вы здесь, то вы точно знаете, о чем мы говорим, и не предложили эффективного решения. Если вам нужно узнать о директивах CSP, мы рекомендуем «погуглить!». Важно реализовать это в своих проектах.

Вам нужно отредактировать эти файлы: next.config.js и pages/_document.tsx

// next.config.js

const crypto = require('crypto')
onst nonce = crypto
  .createHash('sha256')
  .update(crypto.randomUUID())
  .digest('base64')

const isDev = process.env.NODE_ENV !== 'production'

const ContentSecurityPolicy = `
  base-uri 'self';
  default-src 'self';
  script-src 'self' 'nonce-${nonce}' 'strict-dynamic' https: 'unsafe-inline'${
  isDev ? " 'unsafe-eval'" : ''
};
  child-src 'self';
  style-src 'self' 'unsafe-inline';
  font-src 'self' data:;
  connect-src 'self';
  frame-src 'self';
  img-src 'self';
`
module.exports = {
  env: {
    nonce,
  },
  // Adding policies:
  async headers() {
    return [
      {
        headers: [
          {
            key: 'Content-Security-Policy',
            value: ContentSecurityPolicy.replace(/\s{2,}/g, ' ').trim(),
          },
        ],
      },
    ]
  },
}
// pages/_document.tsx

import { Html, Head, Main, NextScript } from 'next/document'
import { cloneElement } from 'react'

class CSPNextScript extends NextScript {
  getScripts(files) {
    return super
      .getScripts(files)
      .map((script) => cloneElement(script, { nonce: process.env.nonce }))
  }
  getPolyfillScripts() {
    return super
      .getPolyfillScripts()
      .map((script) => cloneElement(script, { nonce: process.env.nonce }))
  }
  getDynamicChunks(files) {
    return super
      .getDynamicChunks(files)
      .map((script) => cloneElement(script, { nonce: process.env.nonce }))
  }
}

class CSPHead extends Head {
  getScripts(files) {
    return super
      .getScripts(files)
      .map((script) => cloneElement(script, { nonce: process.env.nonce }))
  }
  getPolyfillScripts() {
    return super
      .getPolyfillScripts()
      .map((script) => cloneElement(script, { nonce: process.env.nonce }))
  }
  getDynamicChunks(files) {
    return super
      .getDynamicChunks(files)
      .map((script) => cloneElement(script, { nonce: process.env.nonce }))
  }
}

export default function Document() {
  return (
    <Html lang="fr">
      <CSPHead />
      <body>
        <Main />
        <CSPNextScript nonce={process.env.nonce} />
      </body>
    </Html>
  )
}

Дополнительный тег scripts в вашем коде должен получить атрибут nonce так же, как мы сделали это для элемента CSPNextScript.

Готово!