NextJS
Reading time: 25 minutes
tip
Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
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 di github.
Architettura Generale di un'Applicazione Next.js
Struttura dei File Tipica
Un progetto Next.js standard segue una specifica struttura di file e directory che facilita le sue funzionalità come il routing, gli endpoint API e la gestione delle risorse statiche. Ecco un layout tipico:
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 risorse statiche come immagini, font e altri file. I file qui sono accessibili al percorso radice (
/
). - app/: Directory centrale per le pagine, layout, componenti e rotte API della tua applicazione. Abbraccia il paradigma App Router, abilitando funzionalità di routing avanzate e segregazione dei componenti server-client.
- app/layout.tsx: Definisce il layout radice per la tua applicazione, avvolgendo tutte le pagine e fornendo elementi UI coerenti come intestazioni, piè di pagina e barre di navigazione.
- app/page.tsx: Funziona come punto di ingresso per la rotta radice
/
, rendendo la home page. - app/[route]/page.tsx: Gestisce rotte statiche e dinamiche. Ogni cartella all'interno di
app/
rappresenta un segmento di rotta, epage.tsx
all'interno di quelle cartelle corrisponde al componente della rotta. - app/api/: Contiene rotte API, consentendo di creare funzioni serverless che gestiscono richieste HTTP. Queste rotte sostituiscono la tradizionale directory
pages/api
. - app/components/: Contiene componenti React riutilizzabili che possono essere utilizzati 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 in tutta l'applicazione.
- .env.local: Memorizza variabili di ambiente specifiche per l'ambiente di sviluppo locale. Queste variabili non sono impegnate nel controllo di versione.
- next.config.js: Personalizza il comportamento di Next.js, incluse le configurazioni webpack, le variabili di ambiente e le 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, gli script e i metadati.
- README.md: Fornisce documentazione e informazioni sul progetto, incluse istruzioni di configurazione, linee guida per l'uso e altri dettagli rilevanti.
- yarn.lock / package-lock.json: Blocca le dipendenze del progetto a versioni specifiche, garantendo installazioni coerenti in ambienti diversi.
Lato Client in Next.js
Routing Basato su File nella Directory app
La directory app
è la pietra angolare del routing nelle ultime versioni di Next.js. Sfrutta il filesystem per definire le rotte, rendendo la gestione delle rotte intuitiva e scalabile.
Gestione del Percorso Radice /
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 radice/
.app/layout.tsx
: Definisce il layout per l'applicazione, avvolgendo 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 del percorso: Il file
page.tsx
direttamente sotto la directoryapp
corrisponde al percorso/
. - Rendering: Questo componente rende il contenuto per la pagina principale.
- Integrazione del layout: Il componente
HomePage
è avvolto dalayout.tsx
, che può includere intestazioni, piè di pagina e altri elementi comuni.
Gestione di altri percorsi statici
Esempio: percorso /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 del percorso: Il file
page.tsx
all'interno della cartellaabout
corrisponde al percorso/about
. - Rendering: Questo componente rende il contenuto per la pagina "about".
Percorsi dinamici
I percorsi dinamici consentono di gestire percorsi con segmenti variabili, permettendo alle applicazioni di visualizzare contenuti basati su parametri come ID, slugs, ecc.
Esempio: Percorso /posts/[id]
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]
denota un segmento dinamico nella rotta, catturando il parametroid
dall'URL. - Accesso ai Parametri: L'oggetto
params
contiene i parametri dinamici, accessibili all'interno del componente. - Corrispondenza delle Rotte: Qualsiasi percorso che corrisponde a
/posts/*
, come/posts/1
,/posts/abc
, ecc., sarà gestito da questo componente.
Rotte Annidate
Next.js supporta il routing annidato, consentendo strutture di rotta gerarchiche che rispecchiano il layout della directory.
Esempio: Rotta /dashboard/settings/profile
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:
- Nesting Profondo: Il file
page.tsx
all'interno didashboard/settings/profile/
corrisponde al percorso/dashboard/settings/profile
. - Riflessione della Gerarchia: La struttura delle directory riflette il percorso URL, migliorando la manutenibilità e la chiarezza.
Route Catch-All
Le route catch-all gestiscono più segmenti nidificati o percorsi sconosciuti, fornendo 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:
- Segmento Catch-All:
[...slug]
cattura tutti i segmenti di percorso rimanenti come un array. - Utilizzo: Utile per gestire scenari di routing dinamico come percorsi generati dagli utenti, categorie annidate, ecc.
- Corrispondenza delle Route: 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 dannosi vengono iniettati in siti web fidati. Gli aggressori possono eseguire script nei browser degli utenti, rubando dati o eseguendo 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 affidabili consente agli attaccanti di iniettare script dannosi.
Iniezione di Template lato Client
Si verifica quando gli input degli utenti non vengono gestiti correttamente nei template, consentendo 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 contenuti dannosi, può portare all'esecuzione di codice non intenzionato.
Client Path Traversal
È una vulnerabilità che consente agli attaccanti di manipolare i percorsi lato client per eseguire azioni non intenzionate, come il Cross-Site Request Forgery (CSRF). A differenza del path traversal lato server, che prende di mira il filesystem del server, il CSPT si concentra sull'esploitazione dei meccanismi lato client per reindirizzare le richieste API legittime a endpoint dannosi.
Esempio di codice vulnerabile:
Un'applicazione Next.js consente agli utenti di caricare e scaricare file. La funzionalità di download è implementata sul 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
- Obiettivo dell'Attaccante: Eseguire un attacco CSRF per eliminare un file critico (ad esempio,
admin/config.json
) manipolando ilfilePath
. - Sfruttamento di CSPT:
- Input Maligno: L'attaccante crea un URL con un
filePath
manipolato come../deleteFile/config.json
. - Chiamata API Risultante: Il codice lato client effettua una richiesta a
/api/files/../deleteFile/config.json
. - Gestione del Server: Se il server non convalida il
filePath
, elabora la richiesta, potenzialmente eliminando o esponendo file sensibili.
- Esecuzione di CSRF:
- Link Creato: L'attaccante invia alla vittima un link o incorpora uno script maligno che attiva la richiesta di download con il
filePath
manipolato. - Risultato: La vittima esegue inconsapevolmente l'azione, portando ad accessi o eliminazioni non autorizzate di file.
Perché È Vulnerabile
- Mancanza di Validazione dell'Input: Il lato client consente input arbitrari di
filePath
, abilitando il path traversal. - Fiducia negli Input del Client: L'API lato server si fida e elabora il
filePath
senza sanitizzazione. - Potenziali Azioni API: Se l'endpoint API esegue azioni che modificano lo stato (ad esempio, eliminare, modificare file), può essere sfruttato tramite CSPT.
Lato Server in Next.js
Rendering Lato Server (SSR)
Le pagine vengono renderizzate sul server ad ogni richiesta, garantendo che l'utente riceva HTML completamente renderizzato. In questo caso, dovresti creare il tuo server personalizzato per elaborare le richieste.
Casi d'Uso:
- Contenuti dinamici che cambiano frequentemente.
- Ottimizzazione SEO, poiché i motori di ricerca possono eseguire la scansione 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
Static Site Generation (SSG)
Le pagine vengono pre-renderizzate al momento della costruzione, risultando in tempi di caricamento più rapidi e un carico ridotto sul server.
Use Cases:
- Contenuti che non cambiano frequentemente.
- Blog, documentazione, pagine di marketing.
Implementation:
// 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 su richiesta senza la necessità di un server dedicato.
Casi d'uso:
- Gestione delle sottomissioni di moduli.
- Interazione con i 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 su file ma introduce capacità migliorate, inclusa la 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 rotte API sono collocate nella directory
app/api/
. - Nomenclatura dei file: Ogni endpoint API risiede nella propria cartella contenente un file
route.js
oroute.ts
. - Funzioni esportate: Invece di un'unica esportazione predefinita, vengono esportate funzioni specifiche per i metodi HTTP (ad es.,
GET
,POST
). - Gestione delle risposte: Utilizza il costruttore
Response
per restituire risposte, consentendo 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 gestori per metodi HTTP specifici all'interno dello stesso file route.js
o route.ts
, promuovendo 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 rotta tramite
params
. - Risposte Migliorate: Maggiore controllo sugli oggetti di risposta, consentendo una gestione precisa degli header e dei codici di stato.
Route Catch-All e Annidate
Next.js 13+ supporta funzionalità di routing avanzate come le route catch-all e le 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:
[...]
denota un segmento catch-all, catturando tutti i percorsi annidati. - Utilizzo: Utile per le API che devono gestire profondità di percorso variabili o segmenti dinamici.
Esempio di Percorsi Annidati:
// 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:
- Profondità di annidamento: Consente strutture API gerarchiche, riflettendo le relazioni tra le risorse.
- Accesso ai parametri: Accesso facile a più parametri di route tramite l'oggetto
params
.
Gestione delle route API in Next.js 12 e versioni precedenti
Route API nella directory pages
(Next.js 12 e versioni precedenti)
Prima che Next.js 13 introducesse la directory app
e migliorasse le capacità di routing, le route API erano principalmente definite all'interno della directory pages
. Questo approccio è ancora ampiamente utilizzato e supportato in Next.js 12 e versioni precedenti.
Route API di base
Struttura dei file:
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 si trovano nella directory
pages/api/
. - Esportazione: Usa
export default
per definire la funzione handler. - Firma della Funzione: L'handler riceve gli oggetti
req
(richiesta HTTP) eres
(risposta HTTP). - Routing: Il nome del file (
hello.js
) corrisponde all'endpoint/api/hello
.
Route API Dinamiche
Struttura del 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
) denotano segmenti di percorso dinamici. - Accesso ai Parametri: Usa
req.query.id
per accedere al parametro dinamico. - Gestione dei Metodi: Utilizza la logica condizionale per gestire i diversi metodi HTTP (
GET
,PUT
,DELETE
, ecc.).
Gestione dei Diversi Metodi HTTP
Mentre l'esempio di percorso API di base gestisce tutti i metodi HTTP all'interno di una singola funzione, puoi strutturare il tuo codice per gestire ogni 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`);
}
}
Best Practices:
- Separation of Concerns: Separare chiaramente la logica per i diversi metodi HTTP.
- Response Consistency: Garantire strutture di risposta coerenti per facilitare la gestione lato client.
- Error Handling: Gestire in modo elegante i metodi non supportati e gli errori imprevisti.
CORS Configuration
Controlla quali origini possono accedere alle tue rotte API, mitigando le vulnerabilità di Cross-Origin Resource Sharing (CORS).
Bad Configuration Example:
// 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 in tutti i percorsi API all'interno del middleware.ts
file:
// 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 dannosi di interagire con la tua API senza restrizioni.- Ampia autorizzazione dei metodi: Consentire tutti i metodi può consentire agli attaccanti di eseguire azioni indesiderate.
Come gli attaccanti lo sfruttano:
Gli attaccanti possono creare siti web dannosi che effettuano richieste alla tua API, potenzialmente abusando di funzionalità come il recupero dei dati, la manipolazione dei dati o l'attivazione di azioni indesiderate per conto di utenti autenticati.
{{#ref}} ../../pentesting-web/cors-bypass.md {{#endref}}
Esposizione del codice del server nel lato client
È facile utilizzare il codice usato dal server anche nel codice esposto e utilizzato dal lato client, il modo migliore per garantire che un file di codice non sia mai esposto nel lato client è utilizzare questo import all'inizio del file:
import "server-only"
File Chiave e 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 elaborata, consentendo 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 (ad es., verifica dell'autenticazione).
- Modifica della Risposta: Può alterare la risposta o passare il controllo al gestore successivo.
Esempi di Casi d'Uso:
- Reindirizzare utenti non autenticati.
- Aggiungere intestazioni personalizzate.
- Registrare richieste.
Configurazione Esemplare:
// 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 ambientali e configurando diverse funzionalità di sicurezza.
Configurazioni di Sicurezza Chiave:
Intestazioni di Sicurezza
Le intestazioni di sicurezza migliorano la sicurezza della tua applicazione istruendo i browser su come gestire i contenuti. Aiutano a mitigare vari attacchi come Cross-Site Scripting (XSS), Clickjacking e sniffing del tipo 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 configurazioni errate possono portare a vulnerabilità di sicurezza, come consentire a fonti non affidabili 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 affidabili o malevoli. Gli attaccanti possono ospitare immagini contenenti payload o contenuti malevoli che ingannano gli utenti.- Un altro problema potrebbe essere quello di consentire un dominio dove chiunque può caricare un'immagine (come
raw.githubusercontent.com
)
Come gli attaccanti ne abusano:
Iniettando immagini da fonti malevole, gli attaccanti possono eseguire attacchi di phishing, visualizzare informazioni fuorvianti o sfruttare vulnerabilità nelle librerie di rendering delle immagini.
Esposizione delle Variabili Ambientali
Gestire informazioni sensibili come chiavi API 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 prefissoNEXT_PUBLIC_
, Next.js non espone le variabili al client. Tuttavia, se erroneamente prefissato (ad es.,NEXT_PUBLIC_SECRET_API_KEY
), diventa accessibile dal lato client.
Come gli attaccanti ne abusano:
Se le variabili sensibili sono esposte al client, gli attaccanti possono recuperarle ispezionando il codice lato client o le richieste di rete, ottenendo accesso non autorizzato a API, database o altri servizi.
Redirects
Gestisci le redirezioni e le riscritture degli URL all'interno della tua applicazione, assicurandoti che gli utenti siano indirizzati in modo appropriato senza introdurre vulnerabilità di redirect aperto.
a. Vulnerabilità di Redirect Aperto
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: Consente agli utenti di specificare qualsiasi URL, abilitando attacchi di reindirizzamento aperto.
- Fiducia nell'Input dell'Utente: I reindirizzamenti a URL forniti dagli utenti senza validazione possono portare a phishing, distribuzione di malware o furto di credenziali.
Come gli attaccanti ne abusano:
Gli attaccanti possono creare URL che sembrano provenire dal tuo dominio ma 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.
Configurazione di Webpack
Personalizza le configurazioni di Webpack per la tua applicazione Next.js, che possono introdurre 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 la possibilità di accesso lato client possono rivelare informazioni riservate.
- Raggruppamento di Segreti: Se file sensibili vengono raggruppati per il client, i loro contenuti diventano accessibili tramite mappe sorgente o ispezionando il codice lato client.
Come gli attaccanti ne abusano:
Gli attaccanti possono accedere o ricostruire la struttura delle directory dell'applicazione, trovando e sfruttando potenzialmente file o dati sensibili.
pages/_app.js
e pages/_document.js
pages/_app.js
Scopo: Sovrascrive il componente App predefinito, consentendo stati globali, stili e componenti di layout.
Casi d'Uso:
- Iniezione di CSS globale.
- Aggiunta di wrapper di layout.
- Integrazione di librerie di 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 Documento predefinito, consentendo 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: Anche se Next.js viene fornito con un server integrato, puoi creare un server personalizzato per casi d'uso avanzati come il routing personalizzato o l'integrazione con servizi backend esistenti.
Nota: L'uso di un server personalizzato può limitare le opzioni di distribuzione, 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 Architettoniche e di Sicurezza Aggiuntive
Variabili d'Ambiente e Configurazione
Scopo: Gestire informazioni sensibili e impostazioni di configurazione al di fuori del codice sorgente.
Migliori Pratiche:
- Usa File
.env
: Memorizza variabili come le chiavi API in.env.local
(escluse dal controllo versione). - Accedi alle Variabili in Modo Sicuro: Usa
process.env.VARIABLE_NAME
per accedere alle variabili d'ambiente. - Non Esporre Mai Segreti sul Client: Assicurati che le variabili sensibili siano utilizzate 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
},
}
Nota: Per limitare le variabili solo al lato server, omettere dal oggetto env
o prefissarle con NEXT_PUBLIC_
per l'esposizione al client.
Autenticazione e Autorizzazione
Approccio:
- Autenticazione Basata su Sessione: Utilizzare i cookie per gestire le sessioni utente.
- Autenticazione Basata su Token: Implementare JWT per un'autenticazione senza stato.
- Fornitori di Terze Parti: Integrare con fornitori OAuth (ad es., Google, GitHub) utilizzando librerie come
next-auth
.
Pratiche di Sicurezza:
- Cookie Sicuri: Impostare gli attributi
HttpOnly
,Secure
eSameSite
. - Hashing delle Password: Eseguire sempre l'hashing delle password prima di memorizzarle.
- Validazione degli Input: Prevenire attacchi di iniezione 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. - Code Splitting: Sfrutta le importazioni dinamiche 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>,
})
tip
Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
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 di github.