NextJS
Reading time: 27 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Soutenir HackTricks
- VĂ©rifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépÎts github.
Architecture Générale d'une Application Next.js
Structure de Fichiers Typique
Un projet Next.js standard suit une structure de fichiers et de répertoires spécifique qui facilite ses fonctionnalités telles que le routage, les points de terminaison API et la gestion des actifs statiques. Voici une disposition typique :
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
RĂ©pertoires et Fichiers Principaux
- public/: HĂ©berge des ressources statiques telles que des images, des polices et d'autres fichiers. Les fichiers ici sont accessibles Ă la racine (
/
). - app/: Répertoire central pour les pages, mises en page, composants et routes API de votre application. Adopte le paradigme App Router, permettant des fonctionnalités de routage avancées et une séparation des composants serveur-client.
- app/layout.tsx: DĂ©finit la mise en page racine de votre application, englobant toutes les pages et fournissant des Ă©lĂ©ments d'interface utilisateur cohĂ©rents comme des en-tĂȘtes, des pieds de page et des barres de navigation.
- app/page.tsx: Sert de point d'entrée pour la route racine
/
, rendant la page d'accueil. - app/[route]/page.tsx: GĂšre les routes statiques et dynamiques. Chaque dossier dans
app/
représente un segment de route, etpage.tsx
dans ces dossiers correspond au composant de la route. - app/api/: Contient des routes API, vous permettant de crĂ©er des fonctions sans serveur qui gĂšrent les requĂȘtes HTTP. Ces routes remplacent le rĂ©pertoire traditionnel
pages/api
. - app/components/: Contient des composants React rĂ©utilisables qui peuvent ĂȘtre utilisĂ©s Ă travers diffĂ©rentes pages et mises en page.
- app/styles/: Contient des fichiers CSS globaux et des modules CSS pour le style spécifique aux composants.
- app/utils/: Inclut des fonctions utilitaires, des modules d'aide et d'autres logiques non-UI qui peuvent ĂȘtre partagĂ©es Ă travers l'application.
- .env.local: Stocke des variables d'environnement spécifiques à l'environnement de développement local. Ces variables ne sont pas engagées dans le contrÎle de version.
- next.config.js: Personnalise le comportement de Next.js, y compris les configurations webpack, les variables d'environnement et les paramÚtres de sécurité.
- tsconfig.json: Configure les paramÚtres TypeScript pour le projet, permettant la vérification de type et d'autres fonctionnalités TypeScript.
- package.json: GÚre les dépendances du projet, les scripts et les métadonnées.
- README.md: Fournit de la documentation et des informations sur le projet, y compris des instructions d'installation, des directives d'utilisation et d'autres détails pertinents.
- yarn.lock / package-lock.json: Verrouille les dépendances du projet à des versions spécifiques, garantissant des installations cohérentes à travers différents environnements.
CÎté Client dans Next.js
Routage Basé sur les Fichiers dans le Répertoire app
Le répertoire app
est la pierre angulaire du routage dans les derniÚres versions de Next.js. Il exploite le systÚme de fichiers pour définir des routes, rendant la gestion des routes intuitive et évolutive.
Gestion du Chemin Racine /
Structure de Fichiers:
my-nextjs-app/
âââ app/
â âââ layout.tsx
â âââ page.tsx
âââ public/
âââ next.config.js
âââ ...
Fichiers Clés :
app/page.tsx
: GĂšre les requĂȘtes vers le chemin racine/
.app/layout.tsx
: DĂ©finit la mise en page de l'application, englobant toutes les pages.
Mise en Ćuvre :
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>
);
}
Explication :
- DĂ©finition de la Route : Le fichier
page.tsx
directement sous le répertoireapp
correspond Ă la route/
. - Rendu : Ce composant rend le contenu de la page d'accueil.
- Intégration de la Mise en Page : Le composant
HomePage
est enveloppé par lelayout.tsx
, qui peut inclure des en-tĂȘtes, des pieds de page et d'autres Ă©lĂ©ments communs.
Gestion d'Autres Chemins Statique
Exemple : Route /about
Structure de Fichier :
arduinoCopy codemy-nextjs-app/
âââ app/
â âââ about/
â â âââ page.tsx
â âââ layout.tsx
â âââ page.tsx
âââ public/
âââ next.config.js
âââ ...
Mise en Ćuvre :
// app/about/page.tsx
export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>Learn more about our mission and values.</p>
</div>
)
}
Explication :
- DĂ©finition de la Route : Le fichier
page.tsx
dans le dossierabout
correspond Ă la route/about
. - Rendu : Ce composant rend le contenu de la page Ă propos.
Routes Dynamiques
Les routes dynamiques permettent de gérer des chemins avec des segments variables, permettant aux applications d'afficher du contenu basé sur des paramÚtres comme des IDs, des slugs, etc.
Exemple : Route /posts/[id]
Structure de Fichiers :
arduinoCopy codemy-nextjs-app/
âââ app/
â âââ posts/
â â âââ [id]/
â â âââ page.tsx
â âââ layout.tsx
â âââ page.tsx
âââ public/
âââ next.config.js
âââ ...
Mise en Ćuvre :
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>
);
}
Explication :
- Segment Dynamique :
[id]
désigne un segment dynamique dans la route, capturant le paramÚtreid
de l'URL. - AccĂšs aux ParamĂštres : L'objet
params
contient les paramĂštres dynamiques, accessibles au sein du composant. - Correspondance de Route : Tout chemin correspondant Ă
/posts/*
, tel que/posts/1
,/posts/abc
, etc., sera géré par ce composant.
Routes Imbriquées
Next.js prend en charge le routage imbriqué, permettant des structures de routes hiérarchiques qui reflÚtent la disposition du répertoire.
Exemple : Route /dashboard/settings/profile
Structure de Fichiers :
arduinoCopy codemy-nextjs-app/
âââ app/
â âââ dashboard/
â â âââ settings/
â â â âââ profile/
â â â âââ page.tsx
â â âââ page.tsx
â âââ layout.tsx
â âââ page.tsx
âââ public/
âââ next.config.js
âââ ...
Mise en Ćuvre :
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>
);
}
Explication :
- Nesting Profond : Le fichier
page.tsx
à l'intérieur dedashboard/settings/profile/
correspond Ă la route/dashboard/settings/profile
. - Réflexion de la Hiérarchie : La structure du répertoire reflÚte le chemin URL, améliorant la maintenabilité et la clarté.
Routes Catch-All
Les routes catch-all gÚrent plusieurs segments imbriqués ou des chemins inconnus, offrant une flexibilité dans la gestion des routes.
Exemple : Route /*
Structure de Fichiers :
my-nextjs-app/
âââ app/
â âââ [..slug]/
â â âââ page.tsx
â âââ layout.tsx
â âââ page.tsx
âââ public/
âââ next.config.js
âââ ...
Mise en Ćuvre :
// 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>
)
}
Explication :
- Segment Catch-All :
[...slug]
capture tous les segments de chemin restants sous forme de tableau. - Utilisation : Utile pour gérer des scénarios de routage dynamique comme des chemins générés par les utilisateurs, des catégories imbriquées, etc.
- Correspondance de Route : Des chemins comme
/anything/here
,/foo/bar/baz
, etc., sont gérés par ce composant.
Vulnérabilités Potentielles CÎté Client
Bien que Next.js fournisse une base sécurisée, des pratiques de codage inappropriées peuvent introduire des vulnérabilités. Les principales vulnérabilités cÎté client incluent :
Cross-Site Scripting (XSS)
Les attaques XSS se produisent lorsque des scripts malveillants sont injectés dans des sites Web de confiance. Les attaquants peuvent exécuter des scripts dans les navigateurs des utilisateurs, volant des données ou effectuant des actions au nom de l'utilisateur.
Exemple de Code Vulnérable :
// Dangerous: Injecting user input directly into HTML
function Comment({ userInput }) {
return <div dangerouslySetInnerHTML={{ __html: userInput }} />
}
Pourquoi c'est vulnérable : L'utilisation de dangerouslySetInnerHTML
avec des entrées non fiables permet aux attaquants d'injecter des scripts malveillants.
Injection de modÚle cÎté client
Se produit lorsque les entrées des utilisateurs ne sont pas correctement gérées dans les modÚles, permettant aux attaquants d'injecter et d'exécuter des modÚles ou des expressions.
Exemple de code vulnérable :
import React from "react"
import ejs from "ejs"
function RenderTemplate({ template, data }) {
const html = ejs.render(template, data)
return <div dangerouslySetInnerHTML={{ __html: html }} />
}
Pourquoi c'est vulnérable : Si template
ou data
inclut du contenu malveillant, cela peut entraßner l'exécution de code non intentionnel.
Traversée de chemin client
C'est une vulnĂ©rabilitĂ© qui permet aux attaquants de manipuler les chemins cĂŽtĂ© client pour effectuer des actions non intentionnelles, telles que le Cross-Site Request Forgery (CSRF). Contrairement Ă la traversĂ©e de chemin cĂŽtĂ© serveur, qui cible le systĂšme de fichiers du serveur, la CSPT se concentre sur l'exploitation des mĂ©canismes cĂŽtĂ© client pour rediriger les requĂȘtes API lĂ©gitimes vers des points de terminaison malveillants.
Exemple de code vulnérable :
Une application Next.js permet aux utilisateurs de tĂ©lĂ©charger et d'uploader des fichiers. La fonctionnalitĂ© de tĂ©lĂ©chargement est implĂ©mentĂ©e cĂŽtĂ© client, oĂč les utilisateurs peuvent spĂ©cifier le chemin du fichier Ă tĂ©lĂ©charger.
// 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>
)
}
Scénario d'Attaque
- Objectif de l'Attaquant : Effectuer une attaque CSRF pour supprimer un fichier critique (par exemple,
admin/config.json
) en manipulant lefilePath
. - Exploitation de CSPT :
- Entrée Malveillante : L'attaquant crée une URL avec un
filePath
manipulé tel que../deleteFile/config.json
. - Appel API RĂ©sultant : Le code cĂŽtĂ© client effectue une requĂȘte Ă
/api/files/../deleteFile/config.json
. - Gestion par le Serveur : Si le serveur ne valide pas le
filePath
, il traite la requĂȘte, pouvant potentiellement supprimer ou exposer des fichiers sensibles.
- Exécution de CSRF :
- Lien Conçu : L'attaquant envoie Ă la victime un lien ou intĂšgre un script malveillant qui dĂ©clenche la requĂȘte de tĂ©lĂ©chargement avec le
filePath
manipulé. - Résultat : La victime exécute sans le savoir l'action, entraßnant un accÚs ou une suppression non autorisée de fichiers.
Pourquoi c'est Vulnérable
- Manque de Validation des Entrées : Le cÎté client permet des entrées
filePath
arbitraires, permettant le parcours de chemin. - Confiance dans les Entrées du Client : L'API cÎté serveur fait confiance et traite le
filePath
sans assainissement. - Actions Potentielles de l'API : Si le point de terminaison de l'API effectue des actions modifiant l'Ă©tat (par exemple, supprimer, modifier des fichiers), il peut ĂȘtre exploitĂ© via CSPT.
CÎté Serveur dans Next.js
Rendu CÎté Serveur (SSR)
Les pages sont rendues sur le serveur Ă chaque requĂȘte, garantissant que l'utilisateur reçoit du HTML entiĂšrement rendu. Dans ce cas, vous devez crĂ©er votre propre serveur personnalisĂ© pour traiter les requĂȘtes.
Cas d'Utilisation :
- Contenu dynamique qui change fréquemment.
- Optimisation SEO, car les moteurs de recherche peuvent explorer la page entiĂšrement rendue.
Mise en Ćuvre :
// 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
Génération de Sites Statique (SSG)
Les pages sont pré-rendues au moment de la construction, ce qui entraßne des temps de chargement plus rapides et une charge serveur réduite.
Cas d'utilisation :
- Contenu qui ne change pas fréquemment.
- Blogs, documentation, pages marketing.
Mise en Ćuvre :
// 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
Fonctions sans serveur (Routes API)
Next.js permet la création de points de terminaison API en tant que fonctions sans serveur. Ces fonctions s'exécutent à la demande sans avoir besoin d'un serveur dédié.
Cas d'utilisation :
- Gestion des soumissions de formulaires.
- Interaction avec des bases de données.
- Traitement des données ou intégration avec des API tierces.
Mise en Ćuvre :
Avec l'introduction du répertoire app
dans Next.js 13, le routage et la gestion des API sont devenus plus flexibles et puissants. Cette approche moderne s'aligne étroitement avec le systÚme de routage basé sur les fichiers mais introduit des capacités améliorées, y compris le support pour les composants serveur et client.
Gestionnaire de route de base
Structure de fichiers :
my-nextjs-app/
âââ app/
â âââ api/
â âââ hello/
â âââ route.js
âââ package.json
âââ ...
Mise en Ćuvre :
// 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))
Explication :
- Emplacement : Les routes API sont placées sous le répertoire
app/api/
. - Nom des fichiers : Chaque point de terminaison API réside dans son propre dossier contenant un fichier
route.js
ouroute.ts
. - Fonctions exportées : Au lieu d'un seul export par défaut, des fonctions spécifiques aux méthodes HTTP (par exemple,
GET
,POST
) sont exportées. - Gestion des réponses : Utilisez le constructeur
Response
pour renvoyer des rĂ©ponses, permettant un meilleur contrĂŽle sur les en-tĂȘtes et les codes d'Ă©tat.
Comment gérer d'autres chemins et méthodes :
Gestion des méthodes HTTP spécifiques
Next.js 13+ vous permet de dĂ©finir des gestionnaires pour des mĂ©thodes HTTP spĂ©cifiques dans le mĂȘme fichier route.js
ou route.ts
, favorisant un code plus clair et mieux organisé.
Exemple :
// 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" },
})
}
Explication :
- Exports Multiples : Chaque méthode HTTP (
GET
,PUT
,DELETE
) a sa propre fonction exportée. - ParamÚtres : Le deuxiÚme argument donne accÚs aux paramÚtres de route via
params
. - RĂ©ponses AmĂ©liorĂ©es : Un meilleur contrĂŽle sur les objets de rĂ©ponse, permettant une gestion prĂ©cise des en-tĂȘtes et des codes d'Ă©tat.
Routes Catch-All et Imbriquées
Next.js 13+ prend en charge des fonctionnalités de routage avancées telles que les routes catch-all et les routes API imbriquées, permettant des structures API plus dynamiques et évolutives.
Exemple de 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" },
})
}
Explication :
- Syntaxe :
[...]
désigne un segment générique, capturant tous les chemins imbriqués. - Utilisation : Utile pour les API qui doivent gérer des profondeurs de route variables ou des segments dynamiques.
Exemple de Routes Imbriquées :
// 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" },
}
)
}
Explication :
- Nesting Profond : Permet des structures API hiérarchiques, reflétant les relations entre les ressources.
- AccÚs aux ParamÚtres : Accédez facilement à plusieurs paramÚtres de route via l'objet
params
.
Gestion des routes API dans Next.js 12 et versions antérieures
Routes API dans le RĂ©pertoire pages
(Next.js 12 et versions antérieures)
Avant que Next.js 13 n'introduise le répertoire app
et n'améliore les capacités de routage, les routes API étaient principalement définies dans le répertoire pages
. Cette approche est toujours largement utilisée et supportée dans Next.js 12 et les versions antérieures.
Route API de Base
Structure de Fichier :
goCopy codemy-nextjs-app/
âââ pages/
â âââ api/
â âââ hello.js
âââ package.json
âââ ...
Mise en Ćuvre :
javascriptCopy code// pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ message: 'Hello, World!' });
}
Explication :
- Emplacement : Les routes API se trouvent sous le répertoire
pages/api/
. - Exportation : Utilisez
export default
pour définir la fonction de gestion. - Signature de la fonction : Le gestionnaire reçoit les objets
req
(requĂȘte HTTP) etres
(réponse HTTP). - Routage : Le nom du fichier (
hello.js
) correspond au point de terminaison/api/hello
.
Routes API Dynamiques
Structure de fichiers :
bashCopy codemy-nextjs-app/
âââ pages/
â âââ api/
â âââ users/
â âââ [id].js
âââ package.json
âââ ...
Mise en Ćuvre :
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`);
}
}
Explication :
- Segments Dynamiques : Les crochets (
[id].js
) désignent des segments de route dynamiques. - Accéder aux ParamÚtres : Utilisez
req.query.id
pour accéder au paramÚtre dynamique. - Gestion des Méthodes : Utilisez une logique conditionnelle pour gérer différentes méthodes HTTP (
GET
,PUT
,DELETE
, etc.).
Gestion des Différentes Méthodes HTTP
Bien que l'exemple de route API de base gÚre toutes les méthodes HTTP dans une seule fonction, vous pouvez structurer votre code pour gérer chaque méthode explicitement pour une meilleure clarté et maintenabilité.
Exemple :
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`);
}
}
Meilleures Pratiques :
- Séparation des Préoccupations : Séparer clairement la logique pour différentes méthodes HTTP.
- Cohérence des Réponses : Assurer des structures de réponse cohérentes pour faciliter la gestion cÎté client.
- Gestion des Erreurs : Gérer avec élégance les méthodes non prises en charge et les erreurs inattendues.
Configuration CORS
ContrÎlez quels origines peuvent accéder à vos routes API, atténuant les vulnérabilités de partage de ressources entre origines (CORS).
Mauvais Exemple de Configuration :
// 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",
},
})
}
Notez que CORS peut Ă©galement ĂȘtre configurĂ© dans toutes les routes API Ă l'intĂ©rieur du middleware.ts
fichier :
// 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
}
ProblĂšme :
Access-Control-Allow-Origin: '*'
: Permet à n'importe quel site web d'accéder à l'API, permettant potentiellement à des sites malveillants d'interagir avec votre API sans restrictions.- Large autorisation de méthode : Autoriser toutes les méthodes peut permettre aux attaquants d'effectuer des actions indésirables.
Comment les attaquants en profitent :
Les attaquants peuvent crĂ©er des sites web malveillants qui effectuent des requĂȘtes Ă votre API, abusant potentiellement de fonctionnalitĂ©s telles que la rĂ©cupĂ©ration de donnĂ©es, la manipulation de donnĂ©es ou le dĂ©clenchement d'actions indĂ©sirables au nom d'utilisateurs authentifiĂ©s.
CORS - Misconfigurations & Bypass
Exposition du code serveur cÎté client
Il est facile de utiliser le code utilisé par le serveur également dans le code exposé et utilisé par le cÎté client, la meilleure façon de s'assurer qu'un fichier de code n'est jamais exposé du cÎté client est d'utiliser cet import au début du fichier :
import "server-only"
Fichiers Clés et Leurs RÎles
middleware.ts
/ middleware.js
Emplacement : Racine du projet ou dans src/
.
But : ExĂ©cute du code dans la fonction sans serveur cĂŽtĂ© serveur avant qu'une requĂȘte ne soit traitĂ©e, permettant des tĂąches comme l'authentification, les redirections ou la modification des rĂ©ponses.
Flux d'Exécution :
- RequĂȘte Entrante : Le middleware intercepte la requĂȘte.
- Traitement : Effectue des opĂ©rations basĂ©es sur la requĂȘte (par exemple, vĂ©rifier l'authentification).
- Modification de la Réponse : Peut altérer la réponse ou passer le contrÎle au gestionnaire suivant.
Cas d'Utilisation Exemples :
- Rediriger les utilisateurs non authentifiés.
- Ajouter des en-tĂȘtes personnalisĂ©s.
- Journaliser les requĂȘtes.
Configuration Exemple :
// 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
Emplacement : Racine du projet.
Objectif : Configure le comportement de Next.js, active ou désactive des fonctionnalités, personnalise les configurations webpack, définit des variables d'environnement et configure plusieurs fonctionnalités de sécurité.
Configurations de sécurité clés :
En-tĂȘtes de sĂ©curitĂ©
Les en-tĂȘtes de sĂ©curitĂ© amĂ©liorent la sĂ©curitĂ© de votre application en instruisant les navigateurs sur la maniĂšre de gĂ©rer le contenu. Ils aident Ă attĂ©nuer diverses attaques telles que le Cross-Site Scripting (XSS), le Clickjacking et le sniffing de type MIME :
- Content Security Policy (CSP)
- X-Frame-Options
- X-Content-Type-Options
- Strict-Transport-Security (HSTS)
- Referrer Policy
Exemples :
// 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...
],
},
]
},
}
ParamĂštres d'optimisation des images
Next.js optimise les images pour la performance, mais des erreurs de configuration peuvent entraßner des vulnérabilités de sécurité, comme permettre à des sources non fiables d'injecter du contenu malveillant.
Exemple de mauvaise configuration :
// next.config.js
module.exports = {
images: {
domains: ["*"], // Allows images from any domain
},
}
ProblĂšme :
'*'
: Permet le chargement d'images depuis n'importe quelle source externe, y compris des domaines non fiables ou malveillants. Les attaquants peuvent hĂ©berger des images contenant des charges utiles malveillantes ou du contenu qui induit les utilisateurs en erreur.- Un autre problĂšme pourrait ĂȘtre de permettre un domaine oĂč tout le monde peut tĂ©lĂ©charger une image (comme
raw.githubusercontent.com
)
Comment les attaquants en abusent :
En injectant des images provenant de sources malveillantes, les attaquants peuvent effectuer des attaques de phishing, afficher des informations trompeuses ou exploiter des vulnérabilités dans les bibliothÚques de rendu d'images.
Exposition des Variables d'Environnement
Gérez les informations sensibles comme les clés API et les identifiants de base de données de maniÚre sécurisée sans les exposer au client.
a. Exposition des Variables Sensibles
Mauvais Exemple de Configuration :
// 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
},
}
ProblĂšme :
SECRET_API_KEY
: Sans le préfixeNEXT_PUBLIC_
, Next.js n'expose pas les variables au client. Cependant, si par erreur il est préfixé (par exemple,NEXT_PUBLIC_SECRET_API_KEY
), il devient accessible cÎté client.
Comment les attaquants en abusent :
Si des variables sensibles sont exposĂ©es au client, les attaquants peuvent les rĂ©cupĂ©rer en inspectant le code cĂŽtĂ© client ou les requĂȘtes rĂ©seau, obtenant un accĂšs non autorisĂ© aux API, bases de donnĂ©es ou autres services.
Redirections
Gérez les redirections et réécritures d'URL au sein de votre application, en veillant à ce que les utilisateurs soient dirigés de maniÚre appropriée sans introduire de vulnérabilités de redirection ouverte.
a. Vulnérabilité de Redirection Ouverte
Mauvais Exemple de Configuration :
// next.config.js
module.exports = {
async redirects() {
return [
{
source: "/redirect",
destination: (req) => req.query.url, // Dynamically redirects based on query parameter
permanent: false,
},
]
},
}
ProblĂšme :
- Destination Dynamique : Permet aux utilisateurs de spécifier n'importe quelle URL, ce qui permet des attaques de redirection ouverte.
- Confiance dans l'Entrée Utilisateur : Les redirections vers des URL fournies par les utilisateurs sans validation peuvent conduire à du phishing, à la distribution de logiciels malveillants ou au vol de données d'identification.
Comment les attaquants en abusent :
Les attaquants peuvent créer des URL qui semblent provenir de votre domaine mais redirigent les utilisateurs vers des sites malveillants. Par exemple :
https://yourdomain.com/redirect?url=https://malicious-site.com
Les utilisateurs faisant confiance au domaine d'origine pourraient naviguer sans le savoir vers des sites Web nuisibles.
Configuration Webpack
Personnalisez les configurations Webpack pour votre application Next.js, ce qui peut introduire involontairement des vulnérabilités de sécurité si cela n'est pas géré avec prudence.
a. Exposition de modules sensibles
Mauvais exemple de configuration :
// next.config.js
module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.alias["@sensitive"] = path.join(__dirname, "secret-folder")
}
return config
},
}
ProblĂšme :
- Exposition de chemins sensibles : L'aliasing de répertoires sensibles et l'autorisation d'accÚs cÎté client peuvent leak des informations confidentielles.
- Regroupement de secrets : Si des fichiers sensibles sont regroupés pour le client, leur contenu devient accessible via des cartes sources ou en inspectant le code cÎté client.
Comment les attaquants en abusent :
Les attaquants peuvent accéder ou reconstruire la structure de répertoires de l'application, trouvant potentiellement et exploitant des fichiers ou des données sensibles.
pages/_app.js
et pages/_document.js
pages/_app.js
But : Remplace le composant App par défaut, permettant un état global, des styles et des composants de mise en page.
Cas d'utilisation :
- Injection de CSS global.
- Ajout d'enveloppes de mise en page.
- Intégration de bibliothÚques de gestion d'état.
Exemple :
// pages/_app.js
import "../styles/globals.css"
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
pages/_document.js
But: Remplace le Document par défaut, permettant la personnalisation des balises HTML et Body.
Cas d'utilisation :
- Modification des balises
<html>
ou<body>
. - Ajout de balises meta ou de scripts personnalisés.
- Intégration de polices tierces.
Exemple :
// 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
Serveur Personnalisé (Optionnel)
But : Bien que Next.js soit livré avec un serveur intégré, vous pouvez créer un serveur personnalisé pour des cas d'utilisation avancés comme le routage personnalisé ou l'intégration avec des services backend existants.
Remarque : L'utilisation d'un serveur personnalisé peut limiter les options de déploiement, en particulier sur des plateformes comme Vercel qui optimisent pour le serveur intégré de Next.js.
Exemple :
// 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")
})
})
Considérations Architecturales et de Sécurité Supplémentaires
Variables d'Environnement et Configuration
Objectif : GĂ©rer les informations sensibles et les paramĂštres de configuration en dehors de la base de code.
Meilleures Pratiques :
- Utiliser des Fichiers
.env
: Stocker des variables comme les clés API dans.env.local
(exclu du contrÎle de version). - Accéder aux Variables de ManiÚre Sécurisée : Utiliser
process.env.VARIABLE_NAME
pour accéder aux variables d'environnement. - Ne Jamais Exposer de Secrets sur le Client : S'assurer que les variables sensibles ne sont utilisées que cÎté serveur.
Exemple :
// 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
},
}
Remarque : Pour restreindre les variables uniquement cÎté serveur, omettez-les de l'objet env
ou préfixez-les avec NEXT_PUBLIC_
pour une exposition cÎté client.
Authentification et Autorisation
Approche :
- Authentification Basée sur les Sessions : Utilisez des cookies pour gérer les sessions utilisateur.
- Authentification Basée sur les Jetons : Implémentez des JWT pour une authentification sans état.
- Fournisseurs Tiers : Intégrez-vous avec des fournisseurs OAuth (par exemple, Google, GitHub) en utilisant des bibliothÚques comme
next-auth
.
Pratiques de Sécurité :
- Cookies Sécurisés : Définissez les attributs
HttpOnly
,Secure
etSameSite
. - Hachage des Mots de Passe : Hachez toujours les mots de passe avant de les stocker.
- Validation des Entrées : Prévenez les attaques par injection en validant et en nettoyant les entrées.
Exemple :
// 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" })
}
}
Optimisation des performances
Stratégies :
- Optimisation des images : Utilisez le composant
next/image
de Next.js pour une optimisation automatique des images. - Fractionnement du code : Profitez des imports dynamiques pour diviser le code et réduire les temps de chargement initiaux.
- Mise en cache : Mettez en Ćuvre des stratĂ©gies de mise en cache pour les rĂ©ponses API et les actifs statiques.
- Chargement paresseux : Chargez les composants ou les actifs uniquement lorsqu'ils sont nécessaires.
Exemple :
// Dynamic Import with Code Splitting
import dynamic from "next/dynamic"
const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
loading: () => <p>Loading...</p>,
})
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Soutenir HackTricks
- VĂ©rifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépÎts github.