Content Security Policy (CSP)
Content Security Policy (CSP) ist wichtig, um Ihre Next.js-Anwendung vor verschiedenen Sicherheitsbedrohungen wie Cross-Site-Scripting (XSS), Clickjacking und anderen Code-Injection-Angriffen 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 eindeutige, zufällige Zeichenfolge, die für die einmalige Verwendung erstellt wird. Es wird in Verbindung mit CSP verwendet, um bestimmte Inline-Skripte oder Styles selektiv auszuführen zu lassen und dabei strenge CSP-Richtlinien zu umgehen.
Warum ein Nonce verwenden?
Obwohl CSPs dafür ausgelegt sind, 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 ausführen zu lassen, wenn sie das korrekte Nonce enthalten.
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 angezeigt wird, sollte ein neues Nonce generiert werden. Das bedeutet, dass Sie dynamisches Rendering verwenden müssen, um Nonces hinzuzufügen.
Beispiel:
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;
`
// Ersetze Zeilenumbrüche und Leerzeichen
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
}
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;
`
// Ersetze Zeilenumbrüche und Leerzeichen
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
}
Standardmäßig wird Middleware bei allen Anfragen ausgeführt. Sie können Middleware mit einem matcher
auf bestimmte Pfade filtern.
Wir empfehlen, Prefetches (von next/link
) und statische Assets, die den CSP-Header nicht benötigen, zu ignorieren.
export const config = {
matcher: [
/*
* Alle Anfragepfade außer denen, die mit folgenden beginnen:
* - 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' },
],
},
],
}
export const config = {
matcher: [
/*
* Alle Anfragepfade außer denen, die mit folgenden beginnen:
* - 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' },
],
},
],
}
Lesen des Nonce
Sie können das Nonce nun aus einer Server-Komponente mit headers
auslesen:
import { headers } from 'next/headers'
import Script from 'next/script'
export default function Page() {
const nonce = headers().get('x-nonce')
return (
<Script
src="https://www.googletagmanager.com/gtag/js"
strategy="afterInteractive"
nonce={nonce}
/>
)
}
import { headers } from 'next/headers'
import Script from 'next/script'
export default function Page() {
const nonce = 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 Ihrer next.config.js
-Datei festlegen:
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, ''),
},
],
},
]
},
}
Versionsverlauf
Wir empfehlen die Verwendung von Next.js v13.4.20+
, um Nonces korrekt zu verarbeiten und anzuwenden.