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

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:

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

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, und page.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:

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

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> ); }

ErklÀrung:

  • Routen-Definition: Die page.tsx-Datei direkt im app-Verzeichnis entspricht der /-Route.
  • Rendering: Diese Komponente rendert den Inhalt fĂŒr die Startseite.
  • Layout-Integration: Die HomePage-Komponente wird von der layout.tsx umschlossen, die Header, Footer und andere gemeinsame Elemente enthalten kann.
Verarbeitung anderer statischer Pfade

Beispiel: /about-Route

Dateistruktur:

arduino
arduinoCopy codemy-nextjs-app/ ├── app/ │ ├── about/ │ │ └── page.tsx │ ├── layout.tsx │ └── page.tsx ├── public/ ├── next.config.js └── ...

Implementierung:

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

ErklÀrung:

  • Routen-Definition: Die page.tsx-Datei im about-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:

arduino
arduinoCopy codemy-nextjs-app/ ├── app/ │ ├── posts/ │ │ └── [id]/ │ │ └── page.tsx │ ├── layout.tsx │ └── page.tsx ├── public/ ├── next.config.js └── ...

Implementierung:

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> ); }

ErklÀrung:

  • Dynamisches Segment: [id] bezeichnet ein dynamisches Segment in der Route, das den id-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:

arduino
arduinoCopy codemy-nextjs-app/ ├── app/ │ ├── dashboard/ │ │ ├── settings/ │ │ │ └── profile/ │ │ │ └── page.tsx │ │ └── page.tsx │ ├── layout.tsx │ └── page.tsx ├── public/ ├── next.config.js └── ...

Implementierung:

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> ); }

ErklÀrung:

  • Tiefe Verschachtelung: Die page.tsx-Datei im Verzeichnis dashboard/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:

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

Implementierung:

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

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:

jsx
// 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:

jsx
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.

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

Angriffsszenario

  1. Ziel des Angreifers: DurchfĂŒhrung eines CSRF-Angriffs, um eine kritische Datei (z.B. admin/config.json) zu löschen, indem der filePath manipuliert wird.
  2. 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.
  1. 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:

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

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:

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

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:

go
my-nextjs-app/ ├── app/ │ └── api/ │ └── hello/ │ └── route.js ├── package.json └── ...

Implementierung:

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))

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 oder route.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:

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

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:

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

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:

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

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:

go
goCopy codemy-nextjs-app/ ├── pages/ │ └── api/ │ └── hello.js ├── package.json └── ...

Implementierung:

javascript
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) und res (HTTP-Antwort).
  • Routing: Der Dateiname (hello.js) entspricht dem Endpunkt /api/hello.

Dynamische API-Routen

Dateistruktur:

bash
bashCopy codemy-nextjs-app/ ├── pages/ │ └── api/ │ └── users/ │ └── [id].js ├── package.json └── ...

Implementierung:

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`); } }

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:

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:

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

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

Beachten Sie, dass CORS auch in allen API-Routen innerhalb der middleware.ts-Datei konfiguriert werden kann:

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 }

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:

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

  1. Eingehende Anfrage: Die Middleware fÀngt die Anfrage ab.
  2. Verarbeitung: FĂŒhrt Operationen basierend auf der Anfrage durch (z. B. ÜberprĂŒfung der Authentifizierung).
  3. 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:

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

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:

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... ], }, ] }, }
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:

javascript
// 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:

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

Problem:

  • SECRET_API_KEY: Ohne das NEXT_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:

javascript
// 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:

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

javascript
// 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:

jsx
// 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:

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

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:

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") }) })

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:

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

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 und SameSite.
  • Passwort-Hashing: Hashen Sie Passwörter immer, bevor Sie sie speichern.
  • Eingabevalidierung: Verhindern Sie Injektionsangriffe, indem Sie Eingaben validieren und bereinigen.

Beispiel:

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" }) } }

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:

jsx
// 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