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

Architettura generale di un’applicazione Next.js

Struttura tipica dei file

Un progetto Next.js standard segue una specifica struttura di file e directory che facilita funzionalitĂ  come routing, API endpoints e gestione delle risorse statiche. 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 dal percorso root (/).
  • app/: Directory centrale per le pagine, i layout, i componenti e le API routes della tua applicazione. Abbraccia il paradigma App Router, abilitando funzionalitĂ  avanzate di routing e la segregazione dei componenti server-client.
  • app/layout.tsx: Definisce il layout radice della tua applicazione, avvolgendo tutte le pagine e fornendo elementi UI coerenti come header, footer e barre di navigazione.
  • app/page.tsx: Serve come punto d’ingresso per la route root /, renderizzando la homepage.
  • app/[route]/page.tsx: Gestisce route statiche e dinamiche. Ogni cartella all’interno di app/ rappresenta un segmento di route, e il page.tsx in quelle cartelle corrisponde al componente della route.
  • app/api/: Contiene API routes, permettendoti di creare serverless functions che gestiscono richieste HTTP. Queste route sostituiscono la tradizionale directory pages/api.
  • app/components/: Ospita 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 utility, moduli helper e altra logica non-UI condivisibile in tutta l’applicazione.
  • .env.local: Memorizza le variabili d’ambiente specifiche per l’ambiente di sviluppo locale. Queste variabili non vengono commesse al controllo versione.
  • next.config.js: Personalizza il comportamento di Next.js, incluse configurazioni webpack, variabili d’ambiente e impostazioni di sicurezza.
  • tsconfig.json: Configura le impostazioni TypeScript per il progetto, abilitando il type checking e altre funzionalitĂ  TypeScript.
  • package.json: Gestisce le dipendenze del progetto, gli script e i 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, assicurando 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 versioni piÚ recenti di Next.js. Sfrutta il filesystem per definire le route, rendendo la gestione delle route intuitiva e scalabile.

Gestire il percorso root /

Struttura dei file:

my-nextjs-app/
├── app/
│   ├── layout.tsx
│   └── page.tsx
├── public/
├── next.config.js
└── ...

File chiave:

  • app/page.tsx: Gestisce le richieste al percorso root /.
  • app/layout.tsx: Definisce il layout dell’applicazione, che avvolge 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.tsx direttamente sotto la directory app corrisponde alla route /.
  • Rendering: Questo componente visualizza il contenuto della pagina principale.
  • Integrazione del layout: Il componente HomePage è avvolto da layout.tsx, che può includere intestazioni, piè di pagina 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 route: Il file page.tsx all’interno della cartella about corrisponde alla route /about.
  • Rendering: Questo componente esegue il rendering del contenuto della pagina about.
Route dinamiche

Le route dinamiche permettono di gestire percorsi con segmenti variabili, consentendo alle applicazioni di visualizzare 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, che cattura il parametro id dall’URL.
  • Accesso ai Parametri: L’oggetto params contiene i parametri dinamici, accessibili all’interno del componente.
  • Matching delle Route: Qualsiasi percorso che corrisponde 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:

  • Nidificazione profonda: The page.tsx file inside dashboard/settings/profile/ corresponds to the /dashboard/settings/profile route.
  • Riflesso della gerarchia: La struttura delle directory riflette il percorso URL, migliorando manutenibilitĂ  e chiarezza.
Rotte catch-all

Le rotte catch-all gestiscono piĂš segmenti nidificati o percorsi sconosciuti, fornendo flessibilitĂ  nella gestione delle route.

Esempio: /* Route

Struttura del 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 rimanenti del percorso come un array.
  • Usage: Utile per gestire scenari di routing dinamico come percorsi generati dagli utenti, categorie nidificate, ecc.
  • Route Matching: Percorsi come /anything/here, /foo/bar/baz, ecc., sono gestiti da questo componente.

Potenziali vulnerabilitĂ  lato client

Sebbene Next.js fornisca 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 aggressori possono eseguire script nei browser degli utenti, rubare dati o eseguire 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’utilizzo di dangerouslySetInnerHTML con input non attendibili consente agli aggressori di iniettare script malevoli.

Client-Side Template Injection

Si verifica quando gli input degli utenti vengono gestiti in modo inappropriato 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 contiene contenuto malevolo, può portare all’esecuzione di codice non previsto.

Client Path Traversal

È una vulnerabilità che permette agli attaccanti di manipolare i percorsi client-side per eseguire azioni non previste, come Cross-Site Request Forgery (CSRF). A differenza del server-side path traversal, che prende di mira il filesystem del server, CSPT si concentra sullo sfruttamento dei meccanismi client-side per reindirizzare 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 lato client, 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 di attacco

  1. Obiettivo dell’attaccante: Perform a CSRF attack to delete a critical file (e.g., admin/config.json) by manipulating the filePath.
  2. Sfruttamento di CSPT:
  • Input maligno: L’attaccante crea una URL con un filePath manipolato come ../deleteFile/config.json.
  • Chiamata API risultante: Il codice client-side effettua una richiesta a /api/files/../deleteFile/config.json.
  • Gestione dal server: Se il server non valida il filePath, elabora la richiesta, potenzialmente cancellando o esponendo file sensibili.
  1. Esecuzione del CSRF:
  • Link creato: L’attaccante invia alla vittima un link o incorpora uno script maligno che forza la richiesta di download con il filePath manipolato.
  • Risultato: La vittima esegue inconsapevolmente l’azione, portando ad accesso o cancellazione non autorizzata di file.

PerchÊ è vulnerabile

  • Mancanza di validazione degli input: Il client-side permette input arbitrari per filePath, abilitando path traversal.
  • Fidarsi degli input dal client: L’API server-side si fida e processa il filePath senza sanitizzazione.
  • Possibili azioni dell’API: Se l’endpoint API esegue azioni che cambiano lo stato (es. delete, modify files), può essere sfruttato via CSPT.

Server-Side in Next.js

Server-Side Rendering (SSR)

Le pagine vengono renderizzate sul server ad ogni richiesta, assicurando che l’utente riceva HTML completamente renderizzato. In questo caso dovresti creare il tuo server personalizzato per processare le richieste.

Casi d’uso:

  • Contenuto dinamico che cambia frequentemente.
  • Ottimizzazione SEO, poichĂŠ i motori di ricerca possono eseguire il crawling 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 vengono pre-renderizzate in fase di build, risultando in tempi di caricamento piĂš rapidi e in un 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:

  • Gestione degli invii di form.
  • Interazione con database.
  • Elaborazione dei dati o integrazione con 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 capacità aumentate, incluso il supporto per componenti server e client.

Basic Route Handler

File Structure:

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: API routes are placed under the app/api/ directory.
  • Nome file: Each API endpoint resides in its own folder containing a route.js or route.ts file.
  • Funzioni esportate: Instead of a single default export, specific HTTP method functions (e.g., GET, POST) are exported.
  • Gestione delle risposte: Use the Response constructor to return responses, allowing more control over headers and status codes.

Come gestire altri percorsi e metodi:

Gestione di metodi HTTP specifici

Next.js 13+ allows you to define handlers for specific HTTP methods within the same route.js or route.ts file, promoting clearer and more organized code.

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: Maggiore controllo sugli oggetti di risposta, consentendo la gestione precisa di header e codici di stato.
Catch-All e route annidate

Next.js 13+ supporta funzionalitĂ  di routing avanzate come route catch-all e route API annidate, consentendo strutture API piĂš dinamiche e scalabili.

Esempio di route 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" },
})
}

Spiegazione:

  • Sintassi: [...] indica un segmento catch-all, che cattura tutti i percorsi annidati.
  • Uso: Utile per API che devono gestire profonditĂ  di route variabili o segmenti dinamici.

Esempio di route nidificate:

// 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, che riflettono le relazioni tra le risorse.
  • Parameter Access: Accedi facilmente a piĂš parametri di route tramite l’oggetto params.
Gestione delle API routes in Next.js 12 e versioni precedenti

API Routes nella directory pages (Next.js 12 e versioni precedenti)

Prima che Next.js 13 introducesse la directory app e le capacità di routing migliorate, le API routes venivano principalmente definite nella directory pages. Questo approccio è ancora ampiamente usato e supportato in Next.js 12 e nelle versioni precedenti.

Route 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:

  • Posizione: Le route API risiedono sotto la directory pages/api/.
  • Export: Usa export default per definire la funzione handler.
  • Firma della funzione: L’handler riceve gli oggetti req (HTTP request) e res (HTTP response).
  • Routing: Il nome del file (hello.js) mappa all’endpoint /api/hello.

Rotte API dinamiche

Struttura dei file:

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:

  • Segmenti dinamici: Le parentesi quadre ([id].js) indicano segmenti di route dinamici.
  • Accesso ai parametri: Usa req.query.id per accedere al parametro dinamico.
  • Gestione dei metodi: Utilizza logica condizionale per gestire i diversi metodi HTTP (GET, PUT, DELETE, ecc.).

Gestione dei diversi metodi HTTP

Mentre l’esempio base di API route gestisce tutti i metodi HTTP all’interno di una singola funzione, puoi strutturare il tuo codice per gestire ciascun metodo esplicitamente per una 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 mantenere strutture di risposta coerenti per facilitare la gestione lato client.
  • Gestione degli errori: Gestisci in modo appropriato i metodi non supportati e gli errori imprevisti.

Configurazione CORS

Controlla quali origini possono accedere alle tue API routes, mitigando le vulnerabilitĂ  legate a 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ò essere configurato anche 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: '*': Permette a qualsiasi sito web di accedere all’API, potenzialmente consentendo a siti malevoli di interagire con la tua API senza restrizioni.
  • Ampia autorizzazione dei metodi: Consentire tutti i metodi può permettere agli attackers di eseguire azioni indesiderate.

Come attackers lo sfruttano:

Gli attackers possono creare siti web malevoli che inviano richieste alla tua API, potenzialmente abusando di funzionalitĂ  come recupero dati, manipolazione dei dati o innescando azioni indesiderate per conto di utenti autenticati.

CORS - Misconfigurations & Bypass

Esposizione del codice server sul lato client

È facile riutilizzare codice usato dal server anche nel codice esposto e utilizzato dal lato client; il modo migliore per assicurarsi che un file di codice non venga mai esposto nel lato client è usare questo import all’inizio del file:

import "server-only"

File principali e i loro ruoli

middleware.ts / middleware.js

Posizione: Radice del progetto o nella cartella src/.

Scopo: Esegue codice nella funzione serverless lato server prima che venga elaborata una richiesta, permettendo attivitĂ  come autenticazione, reindirizzamenti o modifica delle risposte.

Flusso di esecuzione:

  1. Richiesta in entrata: Il middleware intercetta la richiesta.
  2. Elaborazione: Esegue operazioni in base alla richiesta (es. verifica dell’autenticazione).
  3. Modifica della risposta: Può alterare la risposta o trasferire il controllo al handler 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*"],
}

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:

Header di sicurezza

Gli header di sicurezza aumentano la protezione della tua applicazione indicando ai browser come gestire i contenuti. Aiutano a mitigare vari attacchi come Cross-Site Scripting (XSS), Clickjacking e lo sniffing dei tipi MIME:

  • 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 errori di configurazione possono portare a vulnerabilitĂ  di sicurezza, ad esempio permettendo a origini non attendibili di iniettare contenuti malevoli.

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 malevoli. Attackers possono ospitare immagini contenenti payloads malevoli o contenuti che fuorviano gli utenti.
  • Un altro problema potrebbe essere permettere un dominio dove chiunque può caricare un’immagine (come raw.githubusercontent.com)

How attackers abuse it:

Iniettando immagini da fonti malevole, Attackers possono eseguire phishing attacks, mostrare informazioni fuorvianti o sfruttare vulnerabilitĂ  nelle librerie di rendering delle immagini.

Esposizione delle Variabili d'Ambiente

Gestire 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, // Exposed to the client
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL, // Correctly prefixed for client
},
}

Problema:

  • SECRET_API_KEY: Senza il prefisso NEXT_PUBLIC_, Next.js non espone le variabili al client. Tuttavia, se viene prefissato per errore (es. NEXT_PUBLIC_SECRET_API_KEY), diventa accessibile dal lato client.

Come gli attaccanti lo sfruttano:

Se variabili sensibili sono esposte al client, gli attaccanti possono recuperarle ispezionando il codice lato client o le richieste di rete, ottenendo accesso non autorizzato ad API, database o altri servizi.

Reindirizzamenti

Gestisci i reindirizzamenti e le riscritture URL all’interno della tua applicazione, assicurandoti che gli utenti vengano indirizzati correttamente senza introdurre vulnerabilità di open redirect.

a. Open Redirect Vulnerability

Esempio di cattiva configurazione:

// 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, consentendo attacchi di open redirect.
  • Affidarsi ai dati forniti dall’utente: I redirect verso URL forniti dagli utenti senza validazione possono portare a phishing, malware distribution o credential theft.

Come gli attaccanti lo sfruttano:

Gli attaccanti possono creare URL che sembrano provenire dal tuo dominio ma reindirizzano gli utenti su siti malevoli. Per esempio:

https://yourdomain.com/redirect?url=https://malicious-site.com

Gli utenti che si fidano del dominio originale potrebbero navigare inconsapevolmente su siti dannosi.

Configurazione Webpack

Personalizza le configurazioni di Webpack per la tua applicazione Next.js, che possono introdurre involontariamente vulnerabilitĂ  di sicurezza se non gestite 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 leak informazioni confidenziali.
  • Bundling Secrets: Se file sensibili vengono inclusi nel bundle per il client, i loro contenuti diventano accessibili tramite source maps o ispezionando il client-side code.

Come gli attaccanti lo sfruttano:

Gli attaccanti possono accedere o ricostruire la struttura delle directory dell’applicazione, trovando potenzialmente e sfruttando file o dati sensibili.

pages/_app.js e pages/_document.js

pages/_app.js

Scopo: Sovrascrive il componente App predefinito, permettendo stato globale, stili e componenti di layout.

Casi d’uso:

  • Iniettare CSS globale.
  • 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

Scopo: Sovrascrive il Document predefinito, permettendo la personalizzazione dei tag HTML e Body.

Casi d’uso:

  • Modificare i tag <html> o <body>.
  • Aggiungere meta tag o script personalizzati.
  • Integrare font di terze parti.

Esempio:

// 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 venga fornito con un server integrato, puoi creare un server personalizzato per casi d’uso avanzati come routing personalizzato o integrazione con servizi backend esistenti.

Nota: L’utilizzo di un server personalizzato può limitare le opzioni di deployment, specialmente su piattaforme come Vercel che ottimizzano per 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 aggiuntive sull’architettura e la sicurezza

Variabili d’ambiente e configurazione

Scopo: Gestire informazioni sensibili e impostazioni di configurazione al di fuori del codice.

Buone pratiche:

  • Usa file .env: Salva variabili come API keys in .env.local (escluso dal controllo di versione).
  • Accedi alle variabili in modo sicuro: Usa process.env.VARIABLE_NAME per accedere alle variabili d’ambiente.
  • Non esporre mai secrets sul client: Assicurati che le variabili sensibili siano usate solo server-side.

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
},
}

Nota: Per limitare le variabili solo al lato server, omettile dall’oggetto env o prefissale con NEXT_PUBLIC_ per l’esposizione al client.

Autenticazione e Autorizzazione

Approccio:

  • Session-Based Authentication: Usa cookie per gestire le sessioni utente.
  • Token-Based Authentication: Implementa JWTs per autenticazione senza stato.
  • Third-Party Providers: Integra con provider OAuth (es. Google, GitHub) usando librerie come next-auth.

Pratiche di sicurezza:

  • Secure Cookies: Imposta gli attributi HttpOnly, Secure e SameSite.
  • Password Hashing: Esegui sempre l’hash delle password prima di memorizzarle.
  • Input Validation: Previeni attacchi di injection validando e sanificando gli input.

Esempio:

// 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/image di 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 le risorse statiche.
  • Lazy Loading: Carica componenti o risorse 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 Enumerazione (hash to function name via source maps)

Next.js moderno usa “Server Actions” che vengono eseguite sul server ma sono invocate dal client. In produzione queste invocazioni sono opache: tutte le POSTs arrivano su un endpoint comune e sono distinte da un hash specifico della build inviato nell’header Next-Action. Esempio:

POST /
Next-Action: a9f8e2b4c7d1...

Quando productionBrowserSourceMaps è abilitato, i chunk JS minificati contengono chiamate a createServerReference(...) che leak enough structure (più gli source maps associati) per recuperare una mappatura tra l’hash dell’azione e il nome originale della funzione. Questo permette di tradurre gli hash osservati in Next-Action in target concreti come deleteUserAccount() o exportFinancialData().

Approccio di estrazione (regex on minified JS + optional source maps)

Cerca nei 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: server action hash (40+ caratteri esadecimali)
  • Gruppo 2: simbolo o percorso che può essere risolto al nome della funzione originale tramite la source map, se presente

Se lo script pubblicizza una source map (commento trailer //# sourceMappingURL=<...>.map), recuperala e risolvi il simbolo/percorso nel nome della funzione originale.

Flusso di lavoro pratico

  • Scoperta passiva durante la navigazione: cattura le richieste con header Next-Action e gli URL dei JS chunk.
  • Recupera i bundle JS referenziati e i file *.map associati (quando presenti).
  • Esegui la regex sopra per costruire un dizionario hash↔name.
  • Usa il dizionario per indirizzare i test:
  • Name-driven triage (es. transferFunds, exportFinancialData).
  • Monitora la copertura tra le build per nome funzione (gli hash ruotano tra le build).

Esecuzione di hidden actions (richiesta basata su template)

Prendi un POST valido osservato in-proxy come template e sostituisci il valore Next-Action per mirare a un’altra action scoperta:

# Before
Next-Action: a9f8e2b4c7d1

# After
Next-Action: b7e3f9a2d8c5

Riproduci in Repeater e testa authorization, input validation e business logic di azioni altrimenti irraggiungibili.

Automazione di Burp

  • NextjsServerActionAnalyzer (estensione per Burp) automatizza quanto sopra in Burp:
  • Scava nella cronologia del proxy per JS chunks, estrae le voci createServerReference(...), e analizza le source maps quando disponibili.
  • Mantiene un dizionario hash↔function-name ricercabile e rimuove duplicati tra build basandosi sul nome della funzione.
  • Può individuare un POST template valido e aprire una tab di Repeater pronta da inviare con l’hash dell’azione target sostituito.
  • Repo: https://github.com/Adversis/NextjsServerActionAnalyzer

Note e limitazioni

  • Richiede productionBrowserSourceMaps abilitato in produzione per recuperare i nomi dai bundles/source maps.
  • La divulgazione del nome della funzione non è di per sĂŠ una vulnerabilitĂ ; usala per guidare la scoperta e testare l’autorizzazione di ciascuna action.

RCE di deserializzazione del protocollo Flight di React Server Components (CVE-2025-55182)

Le deployment 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 critica server-side prototype pollution durante la deserializzazione dei chunk Flight. Manipolando riferimenti $ all’interno di un payload Flight un attacker può pivotare da prototype polluted a esecuzione arbitraria di JavaScript e quindi a esecuzione di comandi OS nel processo Node.js.

NodeJS - proto & prototype Pollution

Catena d’attacco nei chunk Flight

  1. Primitive di prototype pollution: Imposta "then": "$1:__proto__:then" in modo che il resolver scriva una funzione then su Object.prototype. Qualsiasi plain object processato successivamente diventa thenable, permettendo all’attacker di influenzare il controllo di flusso async all’interno degli internals RSC.
  2. Ricollegamento al costruttore globale Function: Punta _response._formData.get verso "$1:constructor:constructor". Durante la risoluzione, object.constructor → Object, e Object.constructor → Function, quindi chiamate successive a _formData.get() eseguono effettivamente Function(...).
  3. Esecuzione di codice tramite _prefix: Inserisci sorgente JavaScript in _response._prefix. Quando il _formData.get inquinato viene invocato, il framework valuta Function(_prefix)(...), quindi il JS iniettato può eseguire require('child_process').exec() o qualsiasi altro primitivo Node.

Scheletro del payload

{
"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 Functions

Le React Server Functions (RSF) sono qualsiasi funzione che includa la direttiva 'use server';. Ogni form action, mutation, o fetch helper legato a una di queste funzioni diventa un RSC Flight endpoint che deserializzerà senza problemi payload forniti dall’attaccante. Passi di recon utili derivati dalle valutazioni di React2Shell:

  • Inventario statico: cerca la direttiva per capire quante RSF vengono esposte automaticamente dal framework.
rg -n "'use server';" -g"*.{js,ts,jsx,tsx}" app/
  • App Router defaults: create-next-app abilita l’App Router + la directory app/ di default, che silenziosamente trasforma ogni route in un endpoint RSC-capable. App Router assets such as /_next/static/chunks/app/ or responses that stream Flight chunks over text/x-component sono strong Internet-facing fingerprints.
  • Implicitly vulnerable RSC deployments: L’avviso ufficiale di React segnala che le app che distribuiscono il runtime RSC possono essere sfruttabili anche senza RSFs espliciti, quindi considera sospetto qualsiasi build che utilizza react-server-dom-* 19.0.0–19.2.0.
  • Other frameworks bundling RSC: Vite RSC, Parcel RSC, React Router RSC preview, RedwoodSDK, Waku, etc. riutilizzano lo stesso serializzatore e ereditano la medesima superficie di attacco remota finchĂŠ non incorporano build React patchate.

Version coverage (React2Shell)

  • react-server-dom-webpack, react-server-dom-parcel, react-server-dom-turbopack: vulnerabili in 19.0.0, 19.1.0–19.1.1 e 19.2.0; patchate in 19.0.1, 19.1.2 e 19.2.1 rispettivamente.
  • Next.js stable: App Router releases 15.0.0–16.0.6 incorporano lo stack RSC vulnerabile. I patch trains 15.0.5 / 15.1.9 / 15.2.6 / 15.3.6 / 15.4.8 / 15.5.7 / 16.0.7 includono dipendenze corrette, quindi qualsiasi build al di sotto di quelle versioni è di alto valore.
  • Next.js canary: 14.3.0-canary.77+ include anch’esso il runtime buggy e attualmente manca di canary drops patchate, rendendo quei fingerprints candidati forti per lo sfruttamento.

Remote detection oracle

Lo strumento di Assetnote’s react2shell-scanner invia una Flight request multipart creata ad hoc ai percorsi candidati e osserva il comportamento lato server:

  • Default mode esegue un payload RCE deterministico (operazione matematica riflessa via X-Action-Redirect) che prova l’esecuzione di codice.
  • --safe-check mode malforma volontariamente il messaggio Flight in modo che i server patchati rispondano 200/400, mentre i target vulnerabili emettono risposte HTTP/500 contenenti la sottostringa E{"digest" nel body. Questa coppia (500 + digest) è attualmente l’oracolo remoto piĂš affidabile pubblicato dai difensori.
  • Gli switch integrati --waf-bypass, --vercel-waf-bypass, e --windows aggiustano il layout del payload, premettono junk, o sostituiscono comandi OS in modo da poter 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

Riferimenti

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