NextJS
Reading time: 24 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Allgemeine Architektur einer Next.js-Anwendung
Typische Dateistruktur
Ein standardmäßiges Next.js-Projekt folgt einer spezifischen Datei- und Verzeichnisstruktur, die Funktionen wie Routing, API-Endpunkte und die Verwaltung statischer Assets erleichtert. Hier ist ein typisches Layout:
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
Kernverzeichnisse und Dateien
- public/: Beherbergt statische Assets wie Bilder, Schriftarten und andere Dateien. Dateien hier sind am Stammverzeichnis (
/
) zugänglich. - app/: Zentrales Verzeichnis für die Seiten, Layouts, Komponenten und API-Routen Ihrer Anwendung. Umfasst das App Router-Paradigma, das erweiterte Routing-Funktionen und die Trennung von Server- und Client-Komponenten ermöglicht.
- app/layout.tsx: Definiert das Hauptlayout für Ihre Anwendung, das alle Seiten umschließt und konsistente UI-Elemente wie Kopfzeilen, Fußzeilen und Navigationsleisten bereitstellt.
- app/page.tsx: Dient als Einstiegspunkt für die Stammroute
/
, die die Startseite rendert. - app/[route]/page.tsx: Behandelt statische und dynamische Routen. Jedes Verzeichnis innerhalb von
app/
stellt ein Routen-Segment dar, undpage.tsx
innerhalb dieser Verzeichnisse entspricht der Komponente der Route. - app/api/: Enthält API-Routen, die es Ihnen ermöglichen, serverlose Funktionen zu erstellen, die HTTP-Anfragen verarbeiten. Diese Routen ersetzen das traditionelle Verzeichnis
pages/api
. - app/components/: Beherbergt wiederverwendbare React-Komponenten, die in verschiedenen Seiten und Layouts verwendet werden können.
- app/styles/: Enthält globale CSS-Dateien und CSS-Module für komponentenspezifisches Styling.
- app/utils/: Beinhaltet Hilfsfunktionen, Hilfsmodule und andere nicht-UI-Logik, die in der gesamten Anwendung geteilt werden kann.
- .env.local: Speichert Umgebungsvariablen, die spezifisch für die lokale Entwicklungsumgebung sind. Diese Variablen werden nicht in die Versionskontrolle übernommen.
- next.config.js: Passt das Verhalten von Next.js an, einschließlich webpack-Konfigurationen, Umgebungsvariablen und Sicherheitseinstellungen.
- tsconfig.json: Konfiguriert die TypeScript-Einstellungen für das Projekt und ermöglicht Typüberprüfungen und andere TypeScript-Funktionen.
- package.json: Verwaltet die Abhängigkeiten, Skripte und Metadaten des Projekts.
- README.md: Bietet Dokumentation und Informationen über das Projekt, einschließlich Einrichtungsanleitungen, Nutzungshinweisen und anderen relevanten Details.
- yarn.lock / package-lock.json: Sperrt die Abhängigkeiten des Projekts auf bestimmte Versionen, um konsistente Installationen in verschiedenen Umgebungen sicherzustellen.
Client-Seite in Next.js
Dateibasiertes Routing im app
-Verzeichnis
Das app
-Verzeichnis ist das Fundament des Routings in den neuesten Next.js-Versionen. Es nutzt das Dateisystem, um Routen zu definieren, was das Routing-Management intuitiv und skalierbar macht.
Behandlung des Stammverzeichnisses /
Dateistruktur:
my-nextjs-app/
├── app/
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Wichtige Dateien:
app/page.tsx
: Behandelt Anfragen an den Stamm-Pfad/
.app/layout.tsx
: Definiert das Layout für die Anwendung, das alle Seiten umschließt.
Implementierung:
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>
);
}
Erklärung:
- Routen-Definition: Die
page.tsx
-Datei direkt imapp
-Verzeichnis entspricht der/
-Route. - Rendering: Diese Komponente rendert den Inhalt für die Startseite.
- Layout-Integration: Die
HomePage
-Komponente wird von derlayout.tsx
umschlossen, die Header, Footer und andere gemeinsame Elemente enthalten kann.
Verarbeitung anderer statischer Pfade
Beispiel: /about
-Route
Dateistruktur:
arduinoCopy codemy-nextjs-app/
├── app/
│ ├── about/
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Implementierung:
// app/about/page.tsx
export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>Learn more about our mission and values.</p>
</div>
)
}
Erklärung:
- Routen-Definition: Die
page.tsx
-Datei imabout
-Ordner entspricht der/about
-Route. - Rendering: Diese Komponente rendert den Inhalt für die Über-Seite.
Dynamische Routen
Dynamische Routen ermöglichen die Handhabung von Pfaden mit variablen Segmenten, wodurch Anwendungen Inhalte basierend auf Parametern wie IDs, Slugs usw. anzeigen können.
Beispiel: /posts/[id]
Route
Dateistruktur:
arduinoCopy codemy-nextjs-app/
├── app/
│ ├── posts/
│ │ └── [id]/
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Implementierung:
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>
);
}
Erklärung:
- Dynamisches Segment:
[id]
bezeichnet ein dynamisches Segment in der Route, das denid
-Parameter aus der URL erfasst. - Zugriff auf Parameter: Das
params
-Objekt enthält die dynamischen Parameter, die innerhalb der Komponente zugänglich sind. - Routenabgleich: Jeder Pfad, der mit
/posts/*
übereinstimmt, wie z.B./posts/1
,/posts/abc
usw., wird von dieser Komponente verarbeitet.
Verschachtelte Routen
Next.js unterstützt verschachtelte Routen, die hierarchische Routenstrukturen ermöglichen, die der Verzeichnisstruktur entsprechen.
Beispiel: /dashboard/settings/profile
Route
Dateistruktur:
arduinoCopy codemy-nextjs-app/
├── app/
│ ├── dashboard/
│ │ ├── settings/
│ │ │ └── profile/
│ │ │ └── page.tsx
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Implementierung:
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>
);
}
Erklärung:
- Tiefe Verschachtelung: Die
page.tsx
-Datei im Verzeichnisdashboard/settings/profile/
entspricht dem/dashboard/settings/profile
-Pfad. - Hierarchie-Reflexion: Die Verzeichnisstruktur spiegelt den URL-Pfad wider, was die Wartbarkeit und Klarheit verbessert.
Catch-All-Routen
Catch-All-Routen behandeln mehrere verschachtelte Segmente oder unbekannte Pfade und bieten Flexibilität bei der Routenbearbeitung.
Beispiel: /*
Route
Dateistruktur:
my-nextjs-app/
├── app/
│ ├── [..slug]/
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
Implementierung:
// 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>
)
}
Erklärung:
- Catch-All Segment:
[...slug]
erfasst alle verbleibenden Pfadsegmente als Array. - Verwendung: Nützlich für die Handhabung dynamischer Routing-Szenarien wie benutzergenerierte Pfade, verschachtelte Kategorien usw.
- Routenabgleich: Pfade wie
/anything/here
,/foo/bar/baz
usw. werden von dieser Komponente verarbeitet.
Potenzielle Client-Seitige Schwachstellen
Während Next.js eine sichere Grundlage bietet, können unsachgemäße Programmierpraktiken Schwachstellen einführen. Wichtige client-seitige Schwachstellen sind:
Cross-Site Scripting (XSS)
XSS-Angriffe treten auf, wenn bösartige Skripte in vertrauenswürdige Websites injiziert werden. Angreifer können Skripte in den Browsern der Benutzer ausführen, Daten stehlen oder Aktionen im Namen des Benutzers durchführen.
Beispiel für anfälligen Code:
// Dangerous: Injecting user input directly into HTML
function Comment({ userInput }) {
return <div dangerouslySetInnerHTML={{ __html: userInput }} />
}
Warum es anfällig ist: Die Verwendung von dangerouslySetInnerHTML
mit nicht vertrauenswürdigen Eingaben ermöglicht es Angreifern, bösartige Skripte einzuschleusen.
Client-Seitige Template-Injektion
Tritt auf, wenn Benutzereingaben in Templates unsachgemäß behandelt werden, was Angreifern ermöglicht, Templates oder Ausdrücke einzuschleusen und auszuführen.
Beispiel für anfälligen Code:
import React from "react"
import ejs from "ejs"
function RenderTemplate({ template, data }) {
const html = ejs.render(template, data)
return <div dangerouslySetInnerHTML={{ __html: html }} />
}
Warum es anfällig ist: Wenn template
oder data
schädliche Inhalte enthalten, kann dies zur Ausführung unbeabsichtigten Codes führen.
Client Path Traversal
Es handelt sich um eine Schwachstelle, die Angreifern ermöglicht, clientseitige Pfade zu manipulieren, um unbeabsichtigte Aktionen durchzuführen, wie z.B. Cross-Site Request Forgery (CSRF). Im Gegensatz zur serverseitigen Pfadtraversierung, die das Dateisystem des Servers angreift, konzentriert sich CSPT darauf, clientseitige Mechanismen auszunutzen, um legitime API-Anfragen an bösartige Endpunkte umzuleiten.
Beispiel für anfälligen Code:
Eine Next.js-Anwendung ermöglicht es Benutzern, Dateien hochzuladen und herunterzuladen. Die Download-Funktion wird auf der Client-Seite implementiert, wo Benutzer den Dateipfad angeben können, um herunterzuladen.
// 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>
)
}
Angriffsszenario
- Ziel des Angreifers: Durchführung eines CSRF-Angriffs, um eine kritische Datei (z.B.
admin/config.json
) zu löschen, indem derfilePath
manipuliert wird. - Ausnutzung von CSPT:
- Bösartige Eingabe: Der Angreifer erstellt eine URL mit einem manipulierten
filePath
, wie z.B.../deleteFile/config.json
. - Resultierender API-Aufruf: Der clientseitige Code sendet eine Anfrage an
/api/files/../deleteFile/config.json
. - Verarbeitung durch den Server: Wenn der Server den
filePath
nicht validiert, verarbeitet er die Anfrage und könnte potenziell sensible Dateien löschen oder offenlegen.
- Ausführung von CSRF:
- Erstellter Link: Der Angreifer sendet dem Opfer einen Link oder bettet ein bösartiges Skript ein, das die Download-Anfrage mit dem manipulierten
filePath
auslöst. - Ergebnis: Das Opfer führt unwissentlich die Aktion aus, was zu unbefugtem Datei-Zugriff oder -Löschung führt.
Warum es anfällig ist
- Mangelnde Eingabevalidierung: Der clientseitige Code erlaubt beliebige
filePath
-Eingaben, was Pfadtraversal ermöglicht. - Vertrauen auf Client-Eingaben: Die serverseitige API vertraut und verarbeitet den
filePath
ohne Sanitärung. - Potenzielle API-Aktionen: Wenn der API-Endpunkt zustandsverändernde Aktionen durchführt (z.B. löschen, Dateien ändern), kann er über CSPT ausgenutzt werden.
Server-Seite in Next.js
Server-Seitiges Rendering (SSR)
Seiten werden bei jeder Anfrage auf dem Server gerendert, sodass der Benutzer vollständig gerendertes HTML erhält. In diesem Fall sollten Sie Ihren eigenen benutzerdefinierten Server erstellen, um die Anfragen zu verarbeiten.
Anwendungsfälle:
- Dynamische Inhalte, die häufig wechseln.
- SEO-Optimierung, da Suchmaschinen die vollständig gerenderte Seite crawlen können.
Implementierung:
// 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
Statische Seitengenerierung (SSG)
Seiten werden zur Build-Zeit vorgerendert, was zu schnelleren Ladezeiten und einer reduzierten Serverlast führt.
Anwendungsfälle:
- Inhalte, die sich nicht häufig ändern.
- Blogs, Dokumentation, Marketingseiten.
Implementierung:
// 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
Serverless Functions (API-Routen)
Next.js ermöglicht die Erstellung von API-Endpunkten als serverlose Funktionen. Diese Funktionen werden nach Bedarf ausgeführt, ohne dass ein dedizierter Server erforderlich ist.
Anwendungsfälle:
- Verarbeitung von Formularübermittlungen.
- Interaktion mit Datenbanken.
- Datenverarbeitung oder Integration mit Drittanbieter-APIs.
Implementierung:
Mit der Einführung des app
-Verzeichnisses in Next.js 13 sind Routing und API-Verarbeitung flexibler und leistungsfähiger geworden. Dieser moderne Ansatz steht in engem Zusammenhang mit dem dateibasierten Routing-System, bietet jedoch erweiterte Funktionen, einschließlich Unterstützung für Server- und Client-Komponenten.
Grundlegender Routen-Handler
Dateistruktur:
my-nextjs-app/
├── app/
│ └── api/
│ └── hello/
│ └── route.js
├── package.json
└── ...
Implementierung:
// 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))
Erklärung:
- Standort: API-Routen befinden sich im Verzeichnis
app/api/
. - Dateibenennung: Jeder API-Endpunkt befindet sich in seinem eigenen Ordner, der eine
route.js
oderroute.ts
Datei enthält. - Exportierte Funktionen: Anstelle eines einzelnen Standardexports werden spezifische HTTP-Methodenfunktionen (z.B.
GET
,POST
) exportiert. - Antwortverarbeitung: Verwenden Sie den
Response
-Konstruktor, um Antworten zurückzugeben, was mehr Kontrolle über Header und Statuscodes ermöglicht.
So gehen Sie mit anderen Pfaden und Methoden um:
Verarbeitung spezifischer HTTP-Methoden
Next.js 13+ ermöglicht es Ihnen, Handler für spezifische HTTP-Methoden innerhalb derselben route.js
oder route.ts
Datei zu definieren, was klareren und besser organisierten Code fördert.
Beispiel:
// 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" },
})
}
Erklärung:
- Mehrere Exporte: Jede HTTP-Methode (
GET
,PUT
,DELETE
) hat ihre eigene exportierte Funktion. - Parameter: Das zweite Argument ermöglicht den Zugriff auf Routenparameter über
params
. - Erweiterte Antworten: Größere Kontrolle über Antwortobjekte, die eine präzise Verwaltung von Headern und Statuscodes ermöglicht.
Catch-All und Verschachtelte Routen
Next.js 13+ unterstützt erweiterte Routing-Funktionen wie Catch-All-Routen und verschachtelte API-Routen, die dynamischere und skalierbarere API-Strukturen ermöglichen.
Catch-All-Routenbeispiel:
// 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" },
})
}
Erklärung:
- Syntax:
[...]
bezeichnet ein Catch-All-Segment, das alle verschachtelten Pfade erfasst. - Verwendung: Nützlich für APIs, die unterschiedliche Routen-Tiefen oder dynamische Segmente verarbeiten müssen.
Beispiel für verschachtelte Routen:
// 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" },
}
)
}
Erklärung:
- Tiefe Verschachtelung: Ermöglicht hierarchische API-Strukturen, die Ressourcenbeziehungen widerspiegeln.
- Parameterzugriff: Einfacher Zugriff auf mehrere Routenparameter über das
params
-Objekt.
Verarbeitung von API-Routen in Next.js 12 und früher
API-Routen im pages
-Verzeichnis (Next.js 12 und früher)
Bevor Next.js 13 das app
-Verzeichnis und verbesserte Routing-Funktionen einführte, wurden API-Routen hauptsächlich im pages
-Verzeichnis definiert. Dieser Ansatz wird immer noch häufig verwendet und in Next.js 12 und früheren Versionen unterstützt.
Grundlegende API-Route
Dateistruktur:
goCopy codemy-nextjs-app/
├── pages/
│ └── api/
│ └── hello.js
├── package.json
└── ...
Implementierung:
javascriptCopy code// pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ message: 'Hello, World!' });
}
Erklärung:
- Standort: API-Routen befinden sich im Verzeichnis
pages/api/
. - Export: Verwenden Sie
export default
, um die Handler-Funktion zu definieren. - Funktionssignatur: Der Handler erhält die Objekte
req
(HTTP-Anfrage) undres
(HTTP-Antwort). - Routing: Der Dateiname (
hello.js
) entspricht dem Endpunkt/api/hello
.
Dynamische API-Routen
Dateistruktur:
bashCopy codemy-nextjs-app/
├── pages/
│ └── api/
│ └── users/
│ └── [id].js
├── package.json
└── ...
Implementierung:
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`);
}
}
Erklärung:
- Dynamische Segmente: Eckige Klammern (
[id].js
) kennzeichnen dynamische Routen-Segmente. - Zugriff auf Parameter: Verwenden Sie
req.query.id
, um auf den dynamischen Parameter zuzugreifen. - Verarbeitung von Methoden: Nutzen Sie bedingte Logik, um verschiedene HTTP-Methoden (
GET
,PUT
,DELETE
usw.) zu verarbeiten.
Verarbeitung verschiedener HTTP-Methoden
Während das grundlegende API-Routenbeispiel alle HTTP-Methoden innerhalb einer einzigen Funktion behandelt, können Sie Ihren Code so strukturieren, dass jede Methode explizit für bessere Klarheit und Wartbarkeit behandelt wird.
Beispiel:
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:
- Trennung der Anliegen: Logik für verschiedene HTTP-Methoden klar trennen.
- Antwortkonsistenz: Konsistente Antwortstrukturen sicherstellen, um die Handhabung auf der Client-Seite zu erleichtern.
- Fehlerbehandlung: Nicht unterstützte Methoden und unerwartete Fehler elegant behandeln.
CORS-Konfiguration
Steuern Sie, welche Ursprünge auf Ihre API-Routen zugreifen können, um Cross-Origin Resource Sharing (CORS) -Schwachstellen zu mindern.
Schlechtes Konfigurationsbeispiel:
// 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",
},
})
}
Beachten Sie, dass CORS auch in allen API-Routen innerhalb der middleware.ts
-Datei konfiguriert werden kann:
// 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
}
Problem:
Access-Control-Allow-Origin: '*'
: Erlaubt jeder Website den Zugriff auf die API, was potenziell bösartigen Seiten ermöglicht, ohne Einschränkungen mit Ihrer API zu interagieren.- Breite Methoden-Erlaubnis: Das Zulassen aller Methoden kann Angreifern ermöglichen, unerwünschte Aktionen durchzuführen.
Wie Angreifer es ausnutzen:
Angreifer können bösartige Websites erstellen, die Anfragen an Ihre API senden, und dabei Funktionen wie Datenabruf, Datenmanipulation oder das Auslösen unerwünschter Aktionen im Namen authentifizierter Benutzer missbrauchen.
CORS - Misconfigurations & Bypass
Servercode-Exposition auf der Client-Seite
Es kann einfach sein, Code, der vom Server verwendet wird, auch im Code zu verwenden, der auf der Client-Seite exponiert und verwendet wird. Der beste Weg, um sicherzustellen, dass eine Datei mit Code niemals auf der Client-Seite exponiert wird, besteht darin, diesen Import zu Beginn der Datei zu verwenden:
import "server-only"
Schlüsseldateien und ihre Rollen
middleware.ts
/ middleware.js
Standort: Wurzel des Projekts oder innerhalb von src/
.
Zweck: Führt Code in der serverseitigen serverlosen Funktion aus, bevor eine Anfrage verarbeitet wird, was Aufgaben wie Authentifizierung, Weiterleitungen oder das Modifizieren von Antworten ermöglicht.
Ausführungsfluss:
- Eingehende Anfrage: Die Middleware fängt die Anfrage ab.
- Verarbeitung: Führt Operationen basierend auf der Anfrage durch (z. B. Überprüfung der Authentifizierung).
- Antwortmodifikation: Kann die Antwort ändern oder die Kontrolle an den nächsten Handler übergeben.
Beispielanwendungsfälle:
- Umleitung nicht authentifizierter Benutzer.
- Hinzufügen benutzerdefinierter Header.
- Protokollierung von Anfragen.
Beispielkonfiguration:
// 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
Standort: Wurzel des Projekts.
Zweck: Konfiguriert das Verhalten von Next.js, aktiviert oder deaktiviert Funktionen, passt Webpack-Konfigurationen an, setzt Umgebungsvariablen und konfiguriert mehrere Sicherheitsfunktionen.
Wichtige Sicherheitskonfigurationen:
Sicherheitsheader
Sicherheitsheader verbessern die Sicherheit Ihrer Anwendung, indem sie Browser anweisen, wie sie Inhalte behandeln sollen. Sie helfen, verschiedene Angriffe wie Cross-Site Scripting (XSS), Clickjacking und MIME-Typ-Sniffing zu mindern:
- Content Security Policy (CSP)
- X-Frame-Options
- X-Content-Type-Options
- Strict-Transport-Security (HSTS)
- Referrer Policy
Beispiele:
// 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...
],
},
]
},
}
Bildoptimierungseinstellungen
Next.js optimiert Bilder für die Leistung, aber Fehlkonfigurationen können zu Sicherheitsanfälligkeiten führen, wie z.B. das Zulassen von nicht vertrauenswürdigen Quellen, die bösartigen Inhalt injizieren.
Schlechtes Konfigurationsbeispiel:
// next.config.js
module.exports = {
images: {
domains: ["*"], // Allows images from any domain
},
}
Problem:
'*'
: Erlaubt das Laden von Bildern aus jeder externen Quelle, einschließlich untrusted oder bösartiger Domains. Angreifer können Bilder hosten, die bösartige Payloads oder Inhalte enthalten, die Benutzer irreführen.- Ein weiteres Problem könnte sein, eine Domain zuzulassen wo jeder ein Bild hochladen kann (wie
raw.githubusercontent.com
)
Wie Angreifer dies ausnutzen:
Durch das Injizieren von Bildern aus bösartigen Quellen können Angreifer Phishing-Angriffe durchführen, irreführende Informationen anzeigen oder Schwachstellen in Bildrendering-Bibliotheken ausnutzen.
Umgebungsvariablen-Exposition
Verwalten Sie sensible Informationen wie API-Schlüssel und Datenbankanmeldeinformationen sicher, ohne sie dem Client auszusetzen.
a. Exponieren sensibler Variablen
Schlechtes Konfigurationsbeispiel:
// 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
},
}
Problem:
SECRET_API_KEY
: Ohne dasNEXT_PUBLIC_
Präfix gibt Next.js keine Variablen an den Client weiter. Wenn es jedoch fälschlicherweise mit dem Präfix versehen wird (z. B.NEXT_PUBLIC_SECRET_API_KEY
), wird es auf der Client-Seite zugänglich.
Wie Angreifer es ausnutzen:
Wenn sensible Variablen dem Client ausgesetzt sind, können Angreifer sie abrufen, indem sie den Client-seitigen Code oder Netzwerk-Anfragen inspizieren, und unbefugten Zugriff auf APIs, Datenbanken oder andere Dienste erlangen.
Redirects
Verwalten Sie URL-Weiterleitungen und -Umleitungen innerhalb Ihrer Anwendung, um sicherzustellen, dass Benutzer angemessen geleitet werden, ohne offene Redirect-Schwachstellen einzuführen.
a. Open Redirect Vulnerability
Schlechtes Konfigurationsbeispiel:
// next.config.js
module.exports = {
async redirects() {
return [
{
source: "/redirect",
destination: (req) => req.query.url, // Dynamically redirects based on query parameter
permanent: false,
},
]
},
}
Problem:
- Dynamisches Ziel: Ermöglicht es Benutzern, jede URL anzugeben, was offene Umleitungsangriffe ermöglicht.
- Vertrauen auf Benutzereingaben: Umleitungen zu von Benutzern bereitgestellten URLs ohne Validierung können zu Phishing, Malware-Verbreitung oder Diebstahl von Anmeldeinformationen führen.
Wie Angreifer es ausnutzen:
Angreifer können URLs erstellen, die scheinbar von Ihrer Domain stammen, aber Benutzer auf bösartige Seiten umleiten. Zum Beispiel:
https://yourdomain.com/redirect?url=https://malicious-site.com
Benutzer, die der ursprünglichen Domain vertrauen, könnten unwissentlich zu schädlichen Websites navigieren.
Webpack-Konfiguration
Passen Sie die Webpack-Konfigurationen für Ihre Next.js-Anwendung an, die unbeabsichtigt Sicherheitsanfälligkeiten einführen kann, wenn sie nicht vorsichtig behandelt wird.
a. Sensible Module offenlegen
Schlechtes Konfigurationsbeispiel:
// next.config.js
module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.alias["@sensitive"] = path.join(__dirname, "secret-folder")
}
return config
},
}
Problem:
- Exponieren sensibler Pfade: Das Aliasing sensibler Verzeichnisse und das Zulassen des Zugriffs von der Client-Seite können vertrauliche Informationen leaken.
- Bündeln von Geheimnissen: Wenn sensible Dateien für den Client gebündelt werden, werden deren Inhalte über Quellkarten oder durch Inspektion des Client-Seiten-Codes zugänglich.
Wie Angreifer es ausnutzen:
Angreifer können auf die Verzeichnisstruktur der Anwendung zugreifen oder diese rekonstruieren, wodurch sie potenziell sensible Dateien oder Daten finden und ausnutzen können.
pages/_app.js
und pages/_document.js
pages/_app.js
Zweck: Überschreibt die Standard-App-Komponente und ermöglicht globale Zustände, Stile und Layout-Komponenten.
Anwendungsfälle:
- Injizieren von globalem CSS.
- Hinzufügen von Layout-Wrappern.
- Integrieren von Zustandsverwaltungsbibliotheken.
Beispiel:
// pages/_app.js
import "../styles/globals.css"
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
pages/_document.js
Zweck: Überschreibt das Standarddokument und ermöglicht die Anpassung der HTML- und Body-Tags.
Anwendungsfälle:
- Ändern der
<html>
- oder<body>
-Tags. - Hinzufügen von Meta-Tags oder benutzerdefinierten Skripten.
- Integration von Schriftarten von Drittanbietern.
Beispiel:
// 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
Benutzerdefinierter Server (Optional)
Zweck: Während Next.js mit einem integrierten Server geliefert wird, können Sie einen benutzerdefinierten Server für erweiterte Anwendungsfälle wie benutzerdefinierte Routen oder die Integration mit bestehenden Backend-Diensten erstellen.
Hinweis: Die Verwendung eines benutzerdefinierten Servers kann die Bereitstellungsoptionen einschränken, insbesondere auf Plattformen wie Vercel, die für den integrierten Server von Next.js optimieren.
Beispiel:
// 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")
})
})
Zusätzliche architektonische und sicherheitstechnische Überlegungen
Umgebungsvariablen und Konfiguration
Zweck: Sensible Informationen und Konfigurationseinstellungen außerhalb des Codes verwalten.
Best Practices:
- Verwenden Sie
.env
-Dateien: Speichern Sie Variablen wie API-Schlüssel in.env.local
(von der Versionskontrolle ausgeschlossen). - Greifen Sie sicher auf Variablen zu: Verwenden Sie
process.env.VARIABLE_NAME
, um auf Umgebungsvariablen zuzugreifen. - Geheimnisse niemals auf dem Client offenlegen: Stellen Sie sicher, dass sensible Variablen nur serverseitig verwendet werden.
Beispiel:
// 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
},
}
Hinweis: Um Variablen nur auf der Serverseite einzuschränken, lassen Sie sie aus dem env
-Objekt weg oder prefixen Sie sie mit NEXT_PUBLIC_
für die Client-Exposition.
Authentifizierung und Autorisierung
Ansatz:
- Sitzungsbasierte Authentifizierung: Verwenden Sie Cookies zur Verwaltung von Benutzersitzungen.
- Token-basierte Authentifizierung: Implementieren Sie JWTs für zustandslose Authentifizierung.
- Drittanbieter: Integrieren Sie sich mit OAuth-Anbietern (z. B. Google, GitHub) unter Verwendung von Bibliotheken wie
next-auth
.
Sicherheitspraktiken:
- Sichere Cookies: Setzen Sie die Attribute
HttpOnly
,Secure
undSameSite
. - Passwort-Hashing: Hashen Sie Passwörter immer, bevor Sie sie speichern.
- Eingabevalidierung: Verhindern Sie Injektionsangriffe, indem Sie Eingaben validieren und bereinigen.
Beispiel:
// 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" })
}
}
Leistungsoptimierung
Strategien:
- Bildoptimierung: Verwenden Sie die
next/image
Komponente von Next.js für automatische Bildoptimierung. - Code-Splitting: Nutzen Sie dynamische Importe, um Code zu splitten und die anfänglichen Ladezeiten zu reduzieren.
- Caching: Implementieren Sie Caching-Strategien für API-Antworten und statische Assets.
- Lazy Loading: Laden Sie Komponenten oder Assets nur, wenn sie benötigt werden.
Beispiel:
// Dynamic Import with Code Splitting
import dynamic from "next/dynamic"
const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
loading: () => <p>Loading...</p>,
})
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.