Menu

Content Security Policy

Content Security Policy (CSP) ist wichtig, um Ihre Next.js-Anwendung vor verschiedenen Sicherheitsbedrohungen wie Cross-Site-Scripting (XSS), Clickjacking und anderen Code-Injektionsangriffen zu schützen.

Mit CSP können Entwickler festlegen, welche Ursprünge für Inhaltsquellen, Skripte, Stylesheets, Bilder, Schriftarten, Objekte, Medien (Audio, Video), iFrames und mehr zulässig sind.

Beispiele

Nonces

Ein Nonce ist eine einzigartige, zufällige Zeichenfolge, die für die einmalige Verwendung erstellt wird. Er wird in Verbindung mit CSP verwendet, um bestimmte Inline-Skripte oder -Stile selektiv auszuführen und dabei strikte CSP-Direktiven zu umgehen.

Warum einen Nonce verwenden?

Obwohl CSPs entwickelt wurden, um bösartige Skripte zu blockieren, gibt es legitime Szenarien, in denen Inline-Skripte notwendig sind. In solchen Fällen bieten Nonces eine Möglichkeit, diese Skripte auszuführen, wenn sie den korrekten Nonce besitzen.

Hinzufügen eines Nonce mit Middleware

Middleware ermöglicht es Ihnen, Header hinzuzufügen und Nonces zu generieren, bevor die Seite gerendert wird.

Jedes Mal, wenn eine Seite aufgerufen wird, sollte ein neuer Nonce generiert werden. Das bedeutet, dass Sie dynamisches Rendering verwenden müssen, um Nonces hinzuzufügen.

Beispiel:

middleware.ts
import { NextRequest, NextResponse } from 'next/server'
 
export function middleware(request: NextRequest) {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
  const cspHeader = `
    default-src 'self';
    script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
    style-src 'self' 'nonce-${nonce}';
    img-src 'self' blob: data:;
    font-src 'self';
    object-src 'none';
    base-uri 'self';
    form-action 'self';
    frame-ancestors 'none';
    upgrade-insecure-requests;
`
  // Zeilenumbrüche und Leerzeichen ersetzen
  const contentSecurityPolicyHeaderValue = cspHeader
    .replace(/\s{2,}/g, ' ')
    .trim()
 
  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-nonce', nonce)
 
  requestHeaders.set(
    'Content-Security-Policy',
    contentSecurityPolicyHeaderValue
  )
 
  const response = NextResponse.next({
    request: {
      headers: requestHeaders,
    },
  })
  response.headers.set(
    'Content-Security-Policy',
    contentSecurityPolicyHeaderValue
  )
 
  return response
}
middleware.js
import { NextResponse } from 'next/server'
 
export function middleware(request) {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
  const cspHeader = `
    default-src 'self';
    script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
    style-src 'self' 'nonce-${nonce}';
    img-src 'self' blob: data:;
    font-src 'self';
    object-src 'none';
    base-uri 'self';
    form-action 'self';
    frame-ancestors 'none';
    upgrade-insecure-requests;
`
  // Zeilenumbrüche und Leerzeichen ersetzen
  const contentSecurityPolicyHeaderValue = cspHeader
    .replace(/\s{2,}/g, ' ')
    .trim()
 
  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-nonce', nonce)
  requestHeaders.set(
    'Content-Security-Policy',
    contentSecurityPolicyHeaderValue
  )
 
  const response = NextResponse.next({
    request: {
      headers: requestHeaders,
    },
  })
  response.headers.set(
    'Content-Security-Policy',
    contentSecurityPolicyHeaderValue
  )
 
  return response
}

Middleware läuft standardmäßig für alle Anfragen. Sie können Middleware filtern, um nur auf bestimmten Pfaden zu laufen, indem Sie einen matcher verwenden.

Wir empfehlen, Prefetches (von next/link) und statische Assets zu ignorieren, die den CSP-Header nicht benötigen.

middleware.ts
export const config = {
  matcher: [
    /*
     * Alle Anfragepfade außer diejenigen, die beginnen mit:
     * - api (API-Routen)
     * - _next/static (statische Dateien)
     * - _next/image (Bildoptimierungsdateien)
     * - favicon.ico (Favicon-Datei)
     */
    {
      source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
      missing: [
        { type: 'header', key: 'next-router-prefetch' },
        { type: 'header', key: 'purpose', value: 'prefetch' },
      ],
    },
  ],
}
middleware.js
export const config = {
  matcher: [
    /*
     * Alle Anfragepfade außer diejenigen, die beginnen mit:
     * - api (API-Routen)
     * - _next/static (statische Dateien)
     * - _next/image (Bildoptimierungsdateien)
     * - favicon.ico (Favicon-Datei)
     */
    {
      source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
      missing: [
        { type: 'header', key: 'next-router-prefetch' },
        { type: 'header', key: 'purpose', value: 'prefetch' },
      ],
    },
  ],
}

Nonce auslesen

Sie können den Nonce jetzt aus einer Server-Komponente mithilfe von headers auslesen:

app/page.tsx
TypeScript
import { headers } from 'next/headers'
import Script from 'next/script'
 
export default async function Page() {
  const nonce = (await headers()).get('x-nonce')
 
  return (
    <Script
      src="https://www.googletagmanager.com/gtag/js"
      strategy="afterInteractive"
      nonce={nonce}
    />
  )
}

Ohne Nonces

Für Anwendungen, die keine Nonces benötigen, können Sie den CSP-Header direkt in der Datei next.config.js festlegen:

next.config.js
const cspHeader = `
    default-src 'self';
    script-src 'self' 'unsafe-eval' 'unsafe-inline';
    style-src 'self' 'unsafe-inline';
    img-src 'self' blob: data:;
    font-src 'self';
    object-src 'none';
    base-uri 'self';
    form-action 'self';
    frame-ancestors 'none';
    upgrade-insecure-requests;
`
 
module.exports = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: cspHeader.replace(/\n/g, ''),
          },
        ],
      },
    ]
  },
}

Versionshistorie

Wir empfehlen, Version v13.4.20+ von Next.js zu verwenden, um Nonces ordnungsgemäß zu verarbeiten und anzuwenden.