mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
1187 lines
36 KiB
Markdown
1187 lines
36 KiB
Markdown
# NextJS
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|
|
|
|
## 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:
|
|
```lua
|
|
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, e `page.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 diversi ambienti.
|
|
|
|
## 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.
|
|
|
|
<details>
|
|
|
|
<summary>Gestione del Percorso Radice /</summary>
|
|
|
|
**Struttura dei File:**
|
|
```arduino
|
|
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:**
|
|
```tsx
|
|
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 directory `app` corrisponde al percorso `/`.
|
|
- **Rendering:** Questo componente rende il contenuto per la pagina principale.
|
|
- **Integrazione del layout:** Il componente `HomePage` è avvolto da `layout.tsx`, che può includere intestazioni, piè di pagina e altri elementi comuni.
|
|
|
|
</details>
|
|
|
|
<details>
|
|
|
|
<summary>Gestione di altri percorsi statici</summary>
|
|
|
|
**Esempio: percorso `/about`**
|
|
|
|
**Struttura dei file:**
|
|
```arduino
|
|
arduinoCopy codemy-nextjs-app/
|
|
├── app/
|
|
│ ├── about/
|
|
│ │ └── page.tsx
|
|
│ ├── layout.tsx
|
|
│ └── page.tsx
|
|
├── public/
|
|
├── next.config.js
|
|
└── ...
|
|
```
|
|
**Implementazione:**
|
|
```tsx
|
|
// 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 rende il contenuto per la pagina about.
|
|
|
|
</details>
|
|
|
|
<details>
|
|
|
|
<summary>Route Dinamiche</summary>
|
|
|
|
Le route dinamiche consentono di gestire percorsi con segmenti variabili, permettendo alle applicazioni di visualizzare contenuti basati su parametri come ID, slugs, ecc.
|
|
|
|
**Esempio: Route `/posts/[id]`**
|
|
|
|
**Struttura dei File:**
|
|
```arduino
|
|
arduinoCopy codemy-nextjs-app/
|
|
├── app/
|
|
│ ├── posts/
|
|
│ │ └── [id]/
|
|
│ │ └── page.tsx
|
|
│ ├── layout.tsx
|
|
│ └── page.tsx
|
|
├── public/
|
|
├── next.config.js
|
|
└── ...
|
|
```
|
|
**Implementazione:**
|
|
```tsx
|
|
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 parametro `id` 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.
|
|
|
|
</details>
|
|
|
|
<details>
|
|
|
|
<summary>Rotte Annidate</summary>
|
|
|
|
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:**
|
|
```arduino
|
|
arduinoCopy codemy-nextjs-app/
|
|
├── app/
|
|
│ ├── dashboard/
|
|
│ │ ├── settings/
|
|
│ │ │ └── profile/
|
|
│ │ │ └── page.tsx
|
|
│ │ └── page.tsx
|
|
│ ├── layout.tsx
|
|
│ └── page.tsx
|
|
├── public/
|
|
├── next.config.js
|
|
└── ...
|
|
```
|
|
**Implementazione:**
|
|
```tsx
|
|
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 di `dashboard/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.
|
|
|
|
</details>
|
|
|
|
<details>
|
|
|
|
<summary>Route Catch-All</summary>
|
|
|
|
Le route catch-all gestiscono più segmenti nidificati o percorsi sconosciuti, fornendo flessibilità nella gestione delle route.
|
|
|
|
**Esempio: Route `/*`**
|
|
|
|
**Struttura dei File:**
|
|
```arduino
|
|
my-nextjs-app/
|
|
├── app/
|
|
│ ├── [..slug]/
|
|
│ │ └── page.tsx
|
|
│ ├── layout.tsx
|
|
│ └── page.tsx
|
|
├── public/
|
|
├── next.config.js
|
|
└── ...
|
|
```
|
|
**Implementazione:**
|
|
```tsx
|
|
// 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.
|
|
|
|
</details>
|
|
|
|
### Potenziali Vulnerabilità Lato Client
|
|
|
|
Sebbene Next.js fornisca una base sicura, pratiche di codifica improprie possono introdurre vulnerabilità. Le principali vulnerabilità lato client includono:
|
|
|
|
<details>
|
|
|
|
<summary>Cross-Site Scripting (XSS)</summary>
|
|
|
|
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:**
|
|
```jsx
|
|
// 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.
|
|
|
|
</details>
|
|
|
|
<details>
|
|
|
|
<summary>Iniezione di Template lato Client</summary>
|
|
|
|
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:**
|
|
```jsx
|
|
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.
|
|
|
|
</details>
|
|
|
|
<details>
|
|
|
|
<summary>Client Path Traversal</summary>
|
|
|
|
È 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'exploitation 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.
|
|
```jsx
|
|
// 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**: Eseguire un attacco CSRF per eliminare un file critico (ad esempio, `admin/config.json`) manipolando il `filePath`.
|
|
2. **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.
|
|
3. **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.
|
|
|
|
</details>
|
|
|
|
## 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:**
|
|
```jsx
|
|
// 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:**
|
|
```jsx
|
|
// 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:**
|
|
```go
|
|
my-nextjs-app/
|
|
├── app/
|
|
│ └── api/
|
|
│ └── hello/
|
|
│ └── route.js
|
|
├── package.json
|
|
└── ...
|
|
```
|
|
**Implementazione:**
|
|
```javascript
|
|
// 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 si trovano nella directory `app/api/`.
|
|
- **Nomenclatura dei file:** Ogni endpoint API risiede nella propria cartella contenente un file `route.js` o `route.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:
|
|
|
|
<details>
|
|
|
|
<summary>Gestione di metodi HTTP specifici</summary>
|
|
|
|
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:**
|
|
```javascript
|
|
// 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 di rotta tramite `params`.
|
|
- **Risposte Migliorate:** Maggiore controllo sugli oggetti di risposta, consentendo una gestione precisa degli header e dei codici di stato.
|
|
|
|
</details>
|
|
|
|
<details>
|
|
|
|
<summary>Route Catch-All e Annidate</summary>
|
|
|
|
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:**
|
|
```javascript
|
|
// 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:**
|
|
```javascript
|
|
// 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`.
|
|
|
|
</details>
|
|
|
|
<details>
|
|
|
|
<summary>Gestione delle route API in Next.js 12 e versioni precedenti</summary>
|
|
|
|
## 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:**
|
|
```go
|
|
goCopy codemy-nextjs-app/
|
|
├── pages/
|
|
│ └── api/
|
|
│ └── hello.js
|
|
├── package.json
|
|
└── ...
|
|
```
|
|
**Implementazione:**
|
|
```javascript
|
|
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) e `res` (risposta HTTP).
|
|
- **Routing:** Il nome del file (`hello.js`) mappa all'endpoint `/api/hello`.
|
|
|
|
#### Route API Dinamiche
|
|
|
|
**Struttura del File:**
|
|
```bash
|
|
bashCopy codemy-nextjs-app/
|
|
├── pages/
|
|
│ └── api/
|
|
│ └── users/
|
|
│ └── [id].js
|
|
├── package.json
|
|
└── ...
|
|
```
|
|
**Implementazione:**
|
|
```javascript
|
|
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:**
|
|
```javascript
|
|
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.
|
|
|
|
</details>
|
|
|
|
### CORS Configuration
|
|
|
|
Controlla quali origini possono accedere alle tue rotte API, mitigando le vulnerabilità di Cross-Origin Resource Sharing (CORS).
|
|
|
|
**Bad Configuration Example:**
|
|
```javascript
|
|
// 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 file **`middleware.ts`**:
|
|
```javascript
|
|
// 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:
|
|
```js
|
|
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:**
|
|
|
|
1. **Richiesta in Arrivo:** Il middleware intercetta la richiesta.
|
|
2. **Elaborazione:** Esegue operazioni basate sulla richiesta (ad es., verifica dell'autenticazione).
|
|
3. **Modifica della Risposta:** Può alterare la risposta o passare il controllo al gestore successivo.
|
|
|
|
**Esempi di Casi d'Uso:**
|
|
|
|
- Reindirizzamento di utenti non autenticati.
|
|
- Aggiunta di intestazioni personalizzate.
|
|
- Registrazione delle richieste.
|
|
|
|
**Configurazione Esemplare:**
|
|
```typescript
|
|
// 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 di ambiente e configurando diverse funzionalità di sicurezza.
|
|
|
|
**Configurazioni di Sicurezza Chiave:**
|
|
|
|
<details>
|
|
|
|
<summary>Intestazioni di Sicurezza</summary>
|
|
|
|
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:**
|
|
```javascript
|
|
// 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...
|
|
],
|
|
},
|
|
]
|
|
},
|
|
}
|
|
```
|
|
</details>
|
|
|
|
<details>
|
|
|
|
<summary>Impostazioni di Ottimizzazione delle Immagini</summary>
|
|
|
|
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:**
|
|
```javascript
|
|
// 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 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.
|
|
|
|
</details>
|
|
|
|
<details>
|
|
|
|
<summary>Esposizione delle Variabili di Ambiente</summary>
|
|
|
|
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:**
|
|
```javascript
|
|
// 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 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.
|
|
|
|
</details>
|
|
|
|
<details>
|
|
|
|
<summary>Redirects</summary>
|
|
|
|
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:**
|
|
```javascript
|
|
// 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 dannosi. Ad esempio:
|
|
```bash
|
|
https://yourdomain.com/redirect?url=https://malicious-site.com
|
|
```
|
|
Gli utenti che si fidano del dominio originale potrebbero navigare inconsapevolmente verso siti web dannosi.
|
|
|
|
</details>
|
|
|
|
<details>
|
|
|
|
<summary>Configurazione di Webpack</summary>
|
|
|
|
Personalizza le configurazioni di Webpack per la tua applicazione Next.js, che possono involontariamente introdurre vulnerabilità di sicurezza se non gestite con cautela.
|
|
|
|
#### a. Esposizione di Moduli Sensibili
|
|
|
|
**Esempio di Configurazione Errata:**
|
|
```javascript
|
|
// 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 dal lato client possono esporre 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.
|
|
|
|
</details>
|
|
|
|
### `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:**
|
|
```jsx
|
|
// 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:**
|
|
```jsx
|
|
// 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:**
|
|
```javascript
|
|
// 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 i 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:**
|
|
```javascript
|
|
// 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` e `SameSite`.
|
|
- **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:**
|
|
```javascript
|
|
// 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:**
|
|
```jsx
|
|
// Dynamic Import with Code Splitting
|
|
import dynamic from "next/dynamic"
|
|
|
|
const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
|
|
loading: () => <p>Loading...</p>,
|
|
})
|
|
```
|
|
{{#include ../../banners/hacktricks-training.md}}
|