Política de Seguridad de Contenido (CSP)
La Política de Seguridad de Contenido (CSP) es importante para proteger su aplicación Next.js contra diversas amenazas de seguridad como cross-site scripting (XSS), clickjacking y otros ataques de inyección de código.
Al usar CSP, los desarrolladores pueden especificar qué orígenes son permitidos para fuentes de contenido, scripts, hojas de estilo, imágenes, fuentes, objetos, medios (audio, video), iframes y más.
Ejemplos
Nonces
Un nonce es una cadena única y aleatoria de caracteres creada para un uso único. Se utiliza junto con CSP para permitir selectivamente la ejecución de ciertos scripts o estilos en línea, evitando las directivas estrictas de CSP.
¿Por qué usar un nonce?
Aunque las CSP están diseñadas para bloquear scripts maliciosos, hay escenarios legítimos donde los scripts en línea son necesarios. En estos casos, los nonces ofrecen una forma de permitir que estos scripts se ejecuten si tienen el nonce correcto.
Agregar un nonce con Middleware
El Middleware le permite agregar encabezados y generar nonces antes de que se renderice la página.
Cada vez que se visualiza una página, se debe generar un nuevo nonce. Esto significa que debe usar renderizado dinámico para agregar nonces.
Por ejemplo:
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';
block-all-mixed-content;
upgrade-insecure-requests;
`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-nonce', nonce)
requestHeaders.set(
'Content-Security-Policy',
// Reemplaza saltos de línea y espacios
cspHeader.replace(/\s{2,}/g, ' ').trim()
)
return NextResponse.next({
headers: requestHeaders,
request: {
headers: requestHeaders,
},
})
}
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';
block-all-mixed-content;
upgrade-insecure-requests;
`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-nonce', nonce)
requestHeaders.set(
'Content-Security-Policy',
// Reemplaza saltos de línea y espacios
cspHeader.replace(/\s{2,}/g, ' ').trim()
)
return NextResponse.next({
headers: requestHeaders,
request: {
headers: requestHeaders,
},
})
}
Por defecto, el Middleware se ejecuta en todas las solicitudes. Puede filtrar el Middleware para que se ejecute en rutas específicas usando un matcher
.
Recomendamos ignorar las prefetch (de next/link
) y los recursos estáticos que no necesitan el encabezado CSP.
export const config = {
matcher: [
/*
* Coincide con todas las rutas excepto las que comienzan con:
* - api (rutas API)
* - _next/static (archivos estáticos)
* - _next/image (archivos de optimización de imágenes)
* - favicon.ico (archivo favicon)
*/
{
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: [
/*
* Coincide con todas las rutas excepto las que comienzan con:
* - api (rutas API)
* - _next/static (archivos estáticos)
* - _next/image (archivos de optimización de imágenes)
* - favicon.ico (archivo favicon)
*/
{
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
],
}
Leyendo el nonce
Ahora puede leer el nonce desde un Componente de Servidor usando headers
:
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}
/>
)
}
Historial de versiones
Recomendamos usar v13.4.20+
de Next.js para manejar y aplicar correctamente los nonces.