NextJS
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Arquitectura General de una Aplicación Next.js
Estructura de Archivos Típica
Un proyecto estándar de Next.js sigue una estructura específica de archivos y directorios que facilita sus características como enrutamiento, puntos finales de API y gestión de activos estáticos. Aquí hay un diseño típico:
my-nextjs-app/
├── node_modules/
├── public/
│ ├── images/
│ │ └── logo.png
│ └── favicon.ico
├── app/
│ ├── api/
│ │ └── hello/
│ │ └── route.ts
│ ├── layout.tsx
│ ├── page.tsx
│ ├── about/
│ │ └── page.tsx
│ ├── dashboard/
│ │ ├── layout.tsx
│ │ └── page.tsx
│ ├── components/
│ │ ├── Header.tsx
│ │ └── Footer.tsx
│ ├── styles/
│ │ ├── globals.css
│ │ └── Home.module.css
│ └── utils/
│ └── api.ts
├── .env.local
├── next.config.js
├── tsconfig.json
├── package.json
├── README.md
└── yarn.lock / package-lock.json
Directorios y Archivos Principales
- public/: Aloja activos estáticos como imágenes, fuentes y otros archivos. Los archivos aquí son accesibles en la ruta raíz (
/
). - app/: Directorio central para las páginas, diseños, componentes y rutas API de tu aplicación. Abraza el paradigma del App Router, permitiendo características avanzadas de enrutamiento y segregación de componentes del servidor y del cliente.
- app/layout.tsx: Define el diseño raíz para tu aplicación, envolviendo todas las páginas y proporcionando elementos de UI consistentes como encabezados, pies de página y barras de navegación.
- app/page.tsx: Sirve como el punto de entrada para la ruta raíz
/
, renderizando la página de inicio. - app/[route]/page.tsx: Maneja rutas estáticas y dinámicas. Cada carpeta dentro de
app/
representa un segmento de ruta, ypage.tsx
dentro de esas carpetas corresponde al componente de la ruta. - app/api/: Contiene rutas API, permitiéndote crear funciones sin servidor que manejan solicitudes HTTP. Estas rutas reemplazan el directorio tradicional
pages/api
. - app/components/: Alberga componentes reutilizables de React que pueden ser utilizados en diferentes páginas y diseños.
- app/styles/: Contiene archivos CSS globales y Módulos CSS para estilos específicos de componentes.
- app/utils/: Incluye funciones utilitarias, módulos de ayuda y otra lógica no relacionada con la UI que puede ser compartida a través de la aplicación.
- .env.local: Almacena variables de entorno específicas para el entorno de desarrollo local. Estas variables no se comprometen al control de versiones.
- next.config.js: Personaliza el comportamiento de Next.js, incluyendo configuraciones de webpack, variables de entorno y configuraciones de seguridad.
- tsconfig.json: Configura los ajustes de TypeScript para el proyecto, habilitando la verificación de tipos y otras características de TypeScript.
- package.json: Gestiona las dependencias del proyecto, scripts y metadatos.
- README.md: Proporciona documentación e información sobre el proyecto, incluyendo instrucciones de configuración, pautas de uso y otros detalles relevantes.
- yarn.lock / package-lock.json: Bloquea las dependencias del proyecto a versiones específicas, asegurando instalaciones consistentes en diferentes entornos.
Lado del Cliente en Next.js
Enrutamiento Basado en Archivos en el Directorio app
El directorio app
es la piedra angular del enrutamiento en las últimas versiones de Next.js. Aprovecha el sistema de archivos para definir rutas, haciendo que la gestión de rutas sea intuitiva y escalable.
Manejo de la Ruta Raíz /
Estructura de Archivos:
my-nextjs-app/
├── app/
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Archivos Clave:
app/page.tsx
: Maneja las solicitudes a la ruta raíz/
.app/layout.tsx
: Define el diseño para la aplicación, envolviendo todas las páginas.
Implementación:
tsxCopy code// app/page.tsx
export default function HomePage() {
return (
<div>
<h1>Welcome to the Home Page!</h1>
<p>This is the root route.</p>
</div>
);
}
Explicación:
- Definición de Ruta: El archivo
page.tsx
directamente bajo el directorioapp
corresponde a la ruta/
. - Renderizado: Este componente renderiza el contenido para la página de inicio.
- Integración de Diseño: El componente
HomePage
está envuelto por ellayout.tsx
, que puede incluir encabezados, pies de página y otros elementos comunes.
Manejo de Otras Rutas Estáticas
Ejemplo: Ruta /about
Estructura de Archivos:
arduinoCopy codemy-nextjs-app/
├── app/
│ ├── about/
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Implementación:
// app/about/page.tsx
export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>Learn more about our mission and values.</p>
</div>
)
}
Explicación:
- Definición de Ruta: El archivo
page.tsx
dentro de la carpetaabout
corresponde a la ruta/about
. - Renderizado: Este componente renderiza el contenido para la página de acerca de.
Rutas Dinámicas
Las rutas dinámicas permiten manejar rutas con segmentos variables, lo que permite a las aplicaciones mostrar contenido basado en parámetros como IDs, slugs, etc.
Ejemplo: Ruta /posts/[id]
Estructura de Archivos:
arduinoCopy codemy-nextjs-app/
├── app/
│ ├── posts/
│ │ └── [id]/
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Implementación:
tsxCopy code// app/posts/[id]/page.tsx
import { useRouter } from 'next/navigation';
interface PostProps {
params: { id: string };
}
export default function PostPage({ params }: PostProps) {
const { id } = params;
// Fetch post data based on 'id'
return (
<div>
<h1>Post #{id}</h1>
<p>This is the content of post {id}.</p>
</div>
);
}
Explicación:
- Segmento Dinámico:
[id]
denota un segmento dinámico en la ruta, capturando el parámetroid
de la URL. - Accediendo a Parámetros: El objeto
params
contiene los parámetros dinámicos, accesibles dentro del componente. - Coincidencia de Rutas: Cualquier ruta que coincida con
/posts/*
, como/posts/1
,/posts/abc
, etc., será manejada por este componente.
Rutas Anidadas
Next.js soporta el enrutamiento anidado, permitiendo estructuras de rutas jerárquicas que reflejan la disposición del directorio.
Ejemplo: Ruta /dashboard/settings/profile
Estructura de Archivos:
arduinoCopy codemy-nextjs-app/
├── app/
│ ├── dashboard/
│ │ ├── settings/
│ │ │ └── profile/
│ │ │ └── page.tsx
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Implementación:
tsxCopy code// app/dashboard/settings/profile/page.tsx
export default function ProfileSettingsPage() {
return (
<div>
<h1>Profile Settings</h1>
<p>Manage your profile information here.</p>
</div>
);
}
Explicación:
- Anidamiento Profundo: El archivo
page.tsx
dentro dedashboard/settings/profile/
corresponde a la ruta/dashboard/settings/profile
. - Reflejo de Jerarquía: La estructura del directorio refleja la ruta URL, mejorando la mantenibilidad y claridad.
Rutas Catch-All
Las rutas catch-all manejan múltiples segmentos anidados o rutas desconocidas, proporcionando flexibilidad en el manejo de rutas.
Ejemplo: Ruta /*
Estructura de Archivos:
my-nextjs-app/
├── app/
│ ├── [..slug]/
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Implementación:
// app/[...slug]/page.tsx
interface CatchAllProps {
params: { slug: string[] }
}
export default function CatchAllPage({ params }: CatchAllProps) {
const { slug } = params
const fullPath = `/${slug.join("/")}`
return (
<div>
<h1>Catch-All Route</h1>
<p>You have navigated to: {fullPath}</p>
</div>
)
}
Explicación:
- Segmento Catch-All:
[...slug]
captura todos los segmentos de ruta restantes como un array. - Uso: Útil para manejar escenarios de enrutamiento dinámico como rutas generadas por el usuario, categorías anidadas, etc.
- Coincidencia de Rutas: Rutas como
/anything/here
,/foo/bar/baz
, etc., son manejadas por este componente.
Vulnerabilidades Potenciales del Lado del Cliente
Mientras que Next.js proporciona una base segura, las prácticas de codificación inadecuadas pueden introducir vulnerabilidades. Las principales vulnerabilidades del lado del cliente incluyen:
Cross-Site Scripting (XSS)
Los ataques XSS ocurren cuando scripts maliciosos son inyectados en sitios web de confianza. Los atacantes pueden ejecutar scripts en los navegadores de los usuarios, robando datos o realizando acciones en nombre del usuario.
Ejemplo de Código Vulnerable:
// Dangerous: Injecting user input directly into HTML
function Comment({ userInput }) {
return <div dangerouslySetInnerHTML={{ __html: userInput }} />
}
Por qué es vulnerable: Usar dangerouslySetInnerHTML
con entradas no confiables permite a los atacantes inyectar scripts maliciosos.
Inyección de Plantillas del Lado del Cliente
Ocurre cuando las entradas del usuario no se manejan adecuadamente en las plantillas, lo que permite a los atacantes inyectar y ejecutar plantillas o expresiones.
Ejemplo de Código Vulnerable:
import React from "react"
import ejs from "ejs"
function RenderTemplate({ template, data }) {
const html = ejs.render(template, data)
return <div dangerouslySetInnerHTML={{ __html: html }} />
}
Por qué es vulnerable: Si template
o data
incluye contenido malicioso, puede llevar a la ejecución de código no intencionado.
Recorrido de Rutas del Cliente
Es una vulnerabilidad que permite a los atacantes manipular rutas del lado del cliente para realizar acciones no intencionadas, como Cross-Site Request Forgery (CSRF). A diferencia del recorrido de rutas del lado del servidor, que apunta al sistema de archivos del servidor, CSPT se centra en explotar mecanismos del lado del cliente para redirigir solicitudes API legítimas a puntos finales maliciosos.
Ejemplo de Código Vulnerable:
Una aplicación Next.js permite a los usuarios subir y descargar archivos. La función de descarga se implementa del lado del cliente, donde los usuarios pueden especificar la ruta del archivo a descargar.
// pages/download.js
import { useState } from "react"
export default function DownloadPage() {
const [filePath, setFilePath] = useState("")
const handleDownload = () => {
fetch(`/api/files/${filePath}`)
.then((response) => response.blob())
.then((blob) => {
const url = window.URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = filePath
a.click()
})
}
return (
<div>
<h1>Download File</h1>
<input
type="text"
value={filePath}
onChange={(e) => setFilePath(e.target.value)}
placeholder="Enter file path"
/>
<button onClick={handleDownload}>Download</button>
</div>
)
}
Escenario de Ataque
- Objetivo del Atacante: Realizar un ataque CSRF para eliminar un archivo crítico (por ejemplo,
admin/config.json
) manipulando elfilePath
. - Explotando CSPT:
- Entrada Maliciosa: El atacante crea una URL con un
filePath
manipulado como../deleteFile/config.json
. - Llamada API Resultante: El código del lado del cliente realiza una solicitud a
/api/files/../deleteFile/config.json
. - Manejo del Servidor: Si el servidor no valida el
filePath
, procesa la solicitud, potencialmente eliminando o exponiendo archivos sensibles.
- Ejecutando CSRF:
- Enlace Elaborado: El atacante envía a la víctima un enlace o incrusta un script malicioso que activa la solicitud de descarga con el
filePath
manipulado. - Resultado: La víctima ejecuta sin saber la acción, lo que lleva a un acceso no autorizado o eliminación de archivos.
Por Qué Es Vulnerable
- Falta de Validación de Entrada: El lado del cliente permite entradas arbitrarias de
filePath
, habilitando la traversía de rutas. - Confianza en las Entradas del Cliente: La API del lado del servidor confía y procesa el
filePath
sin sanitización. - Acciones Potenciales de la API: Si el endpoint de la API realiza acciones que cambian el estado (por ejemplo, eliminar, modificar archivos), puede ser explotado a través de CSPT.
Lado del Servidor en Next.js
Renderizado del Lado del Servidor (SSR)
Las páginas se renderizan en el servidor en cada solicitud, asegurando que el usuario reciba HTML completamente renderizado. En este caso, deberías crear tu propio servidor personalizado para procesar las solicitudes.
Casos de Uso:
- Contenido dinámico que cambia con frecuencia.
- Optimización SEO, ya que los motores de búsqueda pueden rastrear la página completamente renderizada.
Implementación:
// pages/index.js
export async function getServerSideProps(context) {
const res = await fetch("https://api.example.com/data")
const data = await res.json()
return { props: { data } }
}
function HomePage({ data }) {
return <div>{data.title}</div>
}
export default HomePage
Generación de Sitios Estáticos (SSG)
Las páginas se pre-renderizan en el momento de la construcción, lo que resulta en tiempos de carga más rápidos y una carga reducida en el servidor.
Casos de Uso:
- Contenido que no cambia con frecuencia.
- Blogs, documentación, páginas de marketing.
Implementación:
// pages/index.js
export async function getStaticProps() {
const res = await fetch("https://api.example.com/data")
const data = await res.json()
return { props: { data }, revalidate: 60 } // Revalidate every 60 seconds
}
function HomePage({ data }) {
return <div>{data.title}</div>
}
export default HomePage
Funciones sin servidor (Rutas API)
Next.js permite la creación de puntos finales de API como funciones sin servidor. Estas funciones se ejecutan bajo demanda sin necesidad de un servidor dedicado.
Casos de uso:
- Manejo de envíos de formularios.
- Interacción con bases de datos.
- Procesamiento de datos o integración con APIs de terceros.
Implementación:
Con la introducción del directorio app
en Next.js 13, el enrutamiento y el manejo de API se han vuelto más flexibles y potentes. Este enfoque moderno se alinea estrechamente con el sistema de enrutamiento basado en archivos, pero introduce capacidades mejoradas, incluyendo soporte para componentes de servidor y cliente.
Manejador de ruta básico
Estructura de archivos:
my-nextjs-app/
├── app/
│ └── api/
│ └── hello/
│ └── route.js
├── package.json
└── ...
Implementación:
// app/api/hello/route.js
export async function POST(request) {
return new Response(JSON.stringify({ message: "Hello from App Router!" }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}
// Client-side fetch to access the API endpoint
fetch("/api/submit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "John Doe" }),
})
.then((res) => res.json())
.then((data) => console.log(data))
Explicación:
- Ubicación: Las rutas de la API se colocan en el directorio
app/api/
. - Nomenclatura de Archivos: Cada punto final de la API reside en su propia carpeta que contiene un archivo
route.js
oroute.ts
. - Funciones Exportadas: En lugar de una única exportación predeterminada, se exportan funciones específicas de métodos HTTP (por ejemplo,
GET
,POST
). - Manejo de Respuestas: Utiliza el constructor
Response
para devolver respuestas, lo que permite un mayor control sobre los encabezados y códigos de estado.
Cómo manejar otros caminos y métodos:
Manejo de Métodos HTTP Específicos
Next.js 13+ te permite definir controladores para métodos HTTP específicos dentro del mismo archivo route.js
o route.ts
, promoviendo un código más claro y organizado.
Ejemplo:
// app/api/users/[id]/route.js
export async function GET(request, { params }) {
const { id } = params
// Fetch user data based on 'id'
return new Response(JSON.stringify({ userId: id, name: "Jane Doe" }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}
export async function PUT(request, { params }) {
const { id } = params
// Update user data based on 'id'
return new Response(JSON.stringify({ message: `User ${id} updated.` }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}
export async function DELETE(request, { params }) {
const { id } = params
// Delete user based on 'id'
return new Response(JSON.stringify({ message: `User ${id} deleted.` }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}
Explicación:
- Múltiples Exportaciones: Cada método HTTP (
GET
,PUT
,DELETE
) tiene su propia función exportada. - Parámetros: El segundo argumento proporciona acceso a los parámetros de ruta a través de
params
. - Respuestas Mejoradas: Mayor control sobre los objetos de respuesta, lo que permite una gestión precisa de encabezados y códigos de estado.
Rutas Catch-All y Anidadas
Next.js 13+ admite características avanzadas de enrutamiento como rutas catch-all y rutas API anidadas, lo que permite estructuras de API más dinámicas y escalables.
Ejemplo de Ruta Catch-All:
// app/api/[...slug]/route.js
export async function GET(request, { params }) {
const { slug } = params
// Handle dynamic nested routes
return new Response(JSON.stringify({ slug }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}
Explicación:
- Sintaxis:
[...]
denota un segmento que captura todos los caminos anidados. - Uso: Útil para APIs que necesitan manejar diferentes profundidades de ruta o segmentos dinámicos.
Ejemplo de Rutas Anidadas:
// app/api/posts/[postId]/comments/[commentId]/route.js
export async function GET(request, { params }) {
const { postId, commentId } = params
// Fetch specific comment for a post
return new Response(
JSON.stringify({ postId, commentId, comment: "Great post!" }),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
)
}
Explicación:
- Anidamiento Profundo: Permite estructuras de API jerárquicas, reflejando relaciones de recursos.
- Acceso a Parámetros: Acceso fácil a múltiples parámetros de ruta a través del objeto
params
.
Manejo de rutas API en Next.js 12 y versiones anteriores
Rutas API en el Directorio pages
(Next.js 12 y versiones anteriores)
Antes de que Next.js 13 introdujera el directorio app
y mejorara las capacidades de enrutamiento, las rutas API se definían principalmente dentro del directorio pages
. Este enfoque todavía se utiliza y es compatible en Next.js 12 y versiones anteriores.
Ruta API Básica
Estructura de Archivos:
goCopy codemy-nextjs-app/
├── pages/
│ └── api/
│ └── hello.js
├── package.json
└── ...
Implementación:
javascriptCopy code// pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ message: 'Hello, World!' });
}
Explicación:
- Ubicación: Las rutas de la API residen en el directorio
pages/api/
. - Exportar: Usa
export default
para definir la función manejadora. - Firma de la Función: La función manejadora recibe los objetos
req
(solicitud HTTP) yres
(respuesta HTTP). - Enrutamiento: El nombre del archivo (
hello.js
) se mapea al endpoint/api/hello
.
Rutas de API Dinámicas
Estructura de Archivos:
bashCopy codemy-nextjs-app/
├── pages/
│ └── api/
│ └── users/
│ └── [id].js
├── package.json
└── ...
Implementación:
javascriptCopy code// pages/api/users/[id].js
export default function handler(req, res) {
const {
query: { id },
method,
} = req;
switch (method) {
case 'GET':
// Fetch user data based on 'id'
res.status(200).json({ userId: id, name: 'John Doe' });
break;
case 'PUT':
// Update user data based on 'id'
res.status(200).json({ message: `User ${id} updated.` });
break;
case 'DELETE':
// Delete user based on 'id'
res.status(200).json({ message: `User ${id} deleted.` });
break;
default:
res.setHeader('Allow', ['GET', 'PUT', 'DELETE']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}
Explicación:
- Segmentos Dinámicos: Los corchetes (
[id].js
) denotan segmentos de ruta dinámica. - Accediendo a Parámetros: Usa
req.query.id
para acceder al parámetro dinámico. - Manejo de Métodos: Utiliza lógica condicional para manejar diferentes métodos HTTP (
GET
,PUT
,DELETE
, etc.).
Manejo de Diferentes Métodos HTTP
Mientras que el ejemplo básico de ruta API maneja todos los métodos HTTP dentro de una sola función, puedes estructurar tu código para manejar cada método explícitamente para una mejor claridad y mantenibilidad.
Ejemplo:
javascriptCopy code// pages/api/posts.js
export default async function handler(req, res) {
const { method } = req;
switch (method) {
case 'GET':
// Handle GET request
res.status(200).json({ message: 'Fetching posts.' });
break;
case 'POST':
// Handle POST request
res.status(201).json({ message: 'Post created.' });
break;
default:
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}
Mejores Prácticas:
- Separación de Responsabilidades: Separa claramente la lógica para diferentes métodos HTTP.
- Consistencia en las Respuestas: Asegura estructuras de respuesta consistentes para facilitar el manejo del lado del cliente.
- Manejo de Errores: Maneja de manera elegante los métodos no soportados y los errores inesperados.
Configuración de CORS
Controla qué orígenes pueden acceder a tus rutas de API, mitigando las vulnerabilidades de Cross-Origin Resource Sharing (CORS).
Ejemplo de Mala Configuración:
// app/api/data/route.js
export async function GET(request) {
return new Response(JSON.stringify({ data: "Public Data" }), {
status: 200,
headers: {
"Access-Control-Allow-Origin": "*", // Allows any origin
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
},
})
}
Tenga en cuenta que CORS también se puede configurar en todas las rutas de la API dentro del middleware.ts
archivo:
// app/middleware.ts
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
export function middleware(request: NextRequest) {
const allowedOrigins = [
"https://yourdomain.com",
"https://sub.yourdomain.com",
]
const origin = request.headers.get("Origin")
const response = NextResponse.next()
if (allowedOrigins.includes(origin || "")) {
response.headers.set("Access-Control-Allow-Origin", origin || "")
response.headers.set(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS"
)
response.headers.set(
"Access-Control-Allow-Headers",
"Content-Type, Authorization"
)
// If credentials are needed:
// response.headers.set('Access-Control-Allow-Credentials', 'true');
}
// Handle preflight requests
if (request.method === "OPTIONS") {
return new Response(null, {
status: 204,
headers: response.headers,
})
}
return response
}
export const config = {
matcher: "/api/:path*", // Apply to all API routes
}
Problema:
Access-Control-Allow-Origin: '*'
: Permite que cualquier sitio web acceda a la API, lo que potencialmente permite que sitios maliciosos interactúen con tu API sin restricciones.- Amplia Permisión de Métodos: Permitir todos los métodos puede habilitar a los atacantes para realizar acciones no deseadas.
Cómo los atacantes lo explotan:
Los atacantes pueden crear sitios web maliciosos que hagan solicitudes a tu API, potencialmente abusando de funcionalidades como la recuperación de datos, la manipulación de datos o la activación de acciones no deseadas en nombre de usuarios autenticados.
CORS - Misconfigurations & Bypass
Exposición del código del servidor en el lado del cliente
Es fácil usar código utilizado por el servidor también en el código expuesto y utilizado por el lado del cliente, la mejor manera de asegurar que un archivo de código nunca se exponga en el lado del cliente es utilizando esta importación al principio del archivo:
import "server-only"
Archivos Clave y Sus Roles
middleware.ts
/ middleware.js
Ubicación: Raíz del proyecto o dentro de src/
.
Propósito: Ejecuta código en la función sin servidor del lado del servidor antes de que se procese una solicitud, permitiendo tareas como autenticación, redirecciones o modificación de respuestas.
Flujo de Ejecución:
- Solicitud Entrante: El middleware intercepta la solicitud.
- Procesamiento: Realiza operaciones basadas en la solicitud (por ejemplo, verificar autenticación).
- Modificación de Respuesta: Puede alterar la respuesta o pasar el control al siguiente manejador.
Casos de Uso de Ejemplo:
- Redirigir a usuarios no autenticados.
- Agregar encabezados personalizados.
- Registrar solicitudes.
Configuración de Ejemplo:
// middleware.ts
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
export function middleware(req: NextRequest) {
const url = req.nextUrl.clone()
if (!req.cookies.has("token")) {
url.pathname = "/login"
return NextResponse.redirect(url)
}
return NextResponse.next()
}
export const config = {
matcher: ["/protected/:path*"],
}
next.config.js
Ubicación: Raíz del proyecto.
Propósito: Configura el comportamiento de Next.js, habilitando o deshabilitando características, personalizando configuraciones de webpack, estableciendo variables de entorno y configurando varias características de seguridad.
Configuraciones de Seguridad Clave:
Encabezados de Seguridad
Los encabezados de seguridad mejoran la seguridad de tu aplicación al instruir a los navegadores sobre cómo manejar el contenido. Ayudan a mitigar varios ataques como Cross-Site Scripting (XSS), Clickjacking y la detección de tipos MIME:
- Content Security Policy (CSP)
- X-Frame-Options
- X-Content-Type-Options
- Strict-Transport-Security (HSTS)
- Referrer Policy
Ejemplos:
// next.config.js
module.exports = {
async headers() {
return [
{
source: "/(.*)", // Apply to all routes
headers: [
{
key: "X-Frame-Options",
value: "DENY",
},
{
key: "Content-Security-Policy",
value:
"default-src *; script-src 'self' 'unsafe-inline' 'unsafe-eval';",
},
{
key: "X-Content-Type-Options",
value: "nosniff",
},
{
key: "Strict-Transport-Security",
value: "max-age=63072000; includeSubDomains; preload", // Enforces HTTPS
},
{
key: "Referrer-Policy",
value: "no-referrer", // Completely hides referrer
},
// Additional headers...
],
},
]
},
}
Configuraciones de Optimización de Imágenes
Next.js optimiza las imágenes para el rendimiento, pero las configuraciones incorrectas pueden llevar a vulnerabilidades de seguridad, como permitir que fuentes no confiables inyecten contenido malicioso.
Ejemplo de Configuración Incorrecta:
// next.config.js
module.exports = {
images: {
domains: ["*"], // Allows images from any domain
},
}
Problema:
'*'
: Permite que las imágenes se carguen desde cualquier fuente externa, incluidos dominios no confiables o maliciosos. Los atacantes pueden alojar imágenes que contengan cargas útiles maliciosas o contenido que engañe a los usuarios.- Otro problema podría ser permitir un dominio donde cualquiera puede subir una imagen (como
raw.githubusercontent.com
)
Cómo los atacantes lo abusan:
Al inyectar imágenes de fuentes maliciosas, los atacantes pueden realizar ataques de phishing, mostrar información engañosa o explotar vulnerabilidades en las bibliotecas de renderizado de imágenes.
Exposición de Variables de Entorno
Gestiona información sensible como claves de API y credenciales de base de datos de manera segura sin exponerlas al cliente.
a. Exposición de Variables Sensibles
Ejemplo de Mala Configuración:
// next.config.js
module.exports = {
env: {
SECRET_API_KEY: process.env.SECRET_API_KEY, // Exposed to the client
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL, // Correctly prefixed for client
},
}
Problema:
SECRET_API_KEY
: Sin el prefijoNEXT_PUBLIC_
, Next.js no expone variables al cliente. Sin embargo, si se antepone erróneamente (por ejemplo,NEXT_PUBLIC_SECRET_API_KEY
), se vuelve accesible en el lado del cliente.
Cómo los atacantes lo abusan:
Si las variables sensibles se exponen al cliente, los atacantes pueden recuperarlas inspeccionando el código del lado del cliente o las solicitudes de red, obteniendo acceso no autorizado a APIs, bases de datos u otros servicios.
Redirecciones
Administra las redirecciones y reescrituras de URL dentro de tu aplicación, asegurando que los usuarios sean dirigidos apropiadamente sin introducir vulnerabilidades de redirección abierta.
a. Vulnerabilidad de Redirección Abierta
Ejemplo de Mala Configuración:
// next.config.js
module.exports = {
async redirects() {
return [
{
source: "/redirect",
destination: (req) => req.query.url, // Dynamically redirects based on query parameter
permanent: false,
},
]
},
}
Problema:
- Destino Dinámico: Permite a los usuarios especificar cualquier URL, lo que habilita ataques de redirección abierta.
- Confiar en la Entrada del Usuario: Redirigir a URLs proporcionadas por los usuarios sin validación puede llevar a phishing, distribución de malware o robo de credenciales.
Cómo los atacantes lo abusan:
Los atacantes pueden crear URLs que parecen originarse de tu dominio pero redirigen a los usuarios a sitios maliciosos. Por ejemplo:
https://yourdomain.com/redirect?url=https://malicious-site.com
Los usuarios que confían en el dominio original pueden navegar sin saberlo a sitios web dañinos.
Configuración de Webpack
Personaliza las configuraciones de Webpack para tu aplicación Next.js, que pueden introducir inadvertidamente vulnerabilidades de seguridad si no se manejan con precaución.
a. Exponiendo Módulos Sensibles
Ejemplo de Mala Configuración:
// next.config.js
module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.alias["@sensitive"] = path.join(__dirname, "secret-folder")
}
return config
},
}
Problema:
- Exposición de Rutas Sensibles: Alias de directorios sensibles y permitir el acceso del lado del cliente puede filtrar información confidencial.
- Agrupación de Secretos: Si se agrupan archivos sensibles para el cliente, su contenido se vuelve accesible a través de mapas de origen o inspeccionando el código del lado del cliente.
Cómo los atacantes lo abusan:
Los atacantes pueden acceder o reconstruir la estructura de directorios de la aplicación, potencialmente encontrando y explotando archivos o datos sensibles.
pages/_app.js
y pages/_document.js
pages/_app.js
Propósito: Sobrescribe el componente App predeterminado, permitiendo estado global, estilos y componentes de diseño.
Casos de Uso:
- Inyectar CSS global.
- Agregar envolturas de diseño.
- Integrar bibliotecas de gestión de estado.
Ejemplo:
// pages/_app.js
import "../styles/globals.css"
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
pages/_document.js
Propósito: Sobrescribe el Documento predeterminado, permitiendo la personalización de las etiquetas HTML y Body.
Casos de Uso:
- Modificar las etiquetas
<html>
o<body>
. - Agregar etiquetas meta o scripts personalizados.
- Integrar fuentes de terceros.
Ejemplo:
// pages/_document.js
import Document, { Html, Head, Main, NextScript } from "next/document"
class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>{/* Custom fonts or meta tags */}</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
Servidor Personalizado (Opcional)
Propósito: Aunque Next.js viene con un servidor integrado, puedes crear un servidor personalizado para casos de uso avanzados como enrutamiento personalizado o integración con servicios backend existentes.
Nota: Usar un servidor personalizado puede limitar las opciones de implementación, especialmente en plataformas como Vercel que optimizan para el servidor integrado de Next.js.
Ejemplo:
// server.js
const express = require("express")
const next = require("next")
const dev = process.env.NODE_ENV !== "production"
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = express()
// Custom route
server.get("/a", (req, res) => {
return app.render(req, res, "/a")
})
// Default handler
server.all("*", (req, res) => {
return handle(req, res)
})
server.listen(3000, (err) => {
if (err) throw err
console.log("> Ready on http://localhost:3000")
})
})
Consideraciones Adicionales de Arquitectura y Seguridad
Variables de Entorno y Configuración
Propósito: Gestionar información sensible y configuraciones fuera de la base de código.
Mejores Prácticas:
- Usar Archivos
.env
: Almacenar variables como claves API en.env.local
(excluido del control de versiones). - Acceder a Variables de Forma Segura: Usar
process.env.VARIABLE_NAME
para acceder a las variables de entorno. - Nunca Exponer Secretos en el Cliente: Asegurarse de que las variables sensibles solo se utilicen del lado del servidor.
Ejemplo:
// next.config.js
module.exports = {
env: {
API_KEY: process.env.API_KEY, // Accessible on both client and server
SECRET_KEY: process.env.SECRET_KEY, // Be cautious if accessible on the client
},
}
Nota: Para restringir las variables solo al lado del servidor, omítalas del objeto env
o prefíjalas con NEXT_PUBLIC_
para la exposición del cliente.
Autenticación y Autorización
Enfoque:
- Autenticación Basada en Sesiones: Utiliza cookies para gestionar las sesiones de usuario.
- Autenticación Basada en Tokens: Implementa JWTs para autenticación sin estado.
- Proveedores de Terceros: Integra con proveedores de OAuth (por ejemplo, Google, GitHub) utilizando bibliotecas como
next-auth
.
Prácticas de Seguridad:
- Cookies Seguras: Establece los atributos
HttpOnly
,Secure
ySameSite
. - Hashing de Contraseñas: Siempre hashea las contraseñas antes de almacenarlas.
- Validación de Entradas: Previene ataques de inyección validando y sanitizando las entradas.
Ejemplo:
// pages/api/login.js
import { sign } from "jsonwebtoken"
import { serialize } from "cookie"
export default async function handler(req, res) {
const { username, password } = req.body
// Validate user credentials
if (username === "admin" && password === "password") {
const token = sign({ username }, process.env.JWT_SECRET, {
expiresIn: "1h",
})
res.setHeader(
"Set-Cookie",
serialize("auth", token, {
path: "/",
httpOnly: true,
secure: true,
sameSite: "strict",
})
)
res.status(200).json({ message: "Logged in" })
} else {
res.status(401).json({ error: "Invalid credentials" })
}
}
Optimización del Rendimiento
Estrategias:
- Optimización de Imágenes: Usa el componente
next/image
de Next.js para la optimización automática de imágenes. - División de Código: Aprovecha las importaciones dinámicas para dividir el código y reducir los tiempos de carga iniciales.
- Caché: Implementa estrategias de caché para las respuestas de API y activos estáticos.
- Carga Perezosa: Carga componentes o activos solo cuando sean necesarios.
Ejemplo:
// Dynamic Import with Code Splitting
import dynamic from "next/dynamic"
const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
loading: () => <p>Loading...</p>,
})
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.