NextJS
Reading time: 23 minutes
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Загальна архітектура програми Next.js
Типова структура файлів
Стандартний проект Next.js має специфічну структуру файлів і каталогів, яка полегшує його функції, такі як маршрутизація, API кінцеві точки та управління статичними активами. Ось типове розташування:
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
Основні каталоги та файли
- public/: Зберігає статичні ресурси, такі як зображення, шрифти та інші файли. Файли тут доступні за кореневим шляхом (
/
). - app/: Центральний каталог для сторінок, макетів, компонентів та API маршрутів вашого додатку. Впроваджує парадигму App Router, що дозволяє використовувати розширені функції маршрутизації та сегрегацію компонентів серверу та клієнту.
- app/layout.tsx: Визначає кореневий макет для вашого додатку, обгортаючи всі сторінки та надаючи послідовні елементи UI, такі як заголовки, підвал та навігаційні панелі.
- app/page.tsx: Служить точкою входу для кореневого маршруту
/
, рендерячи домашню сторінку. - app/[route]/page.tsx: Обробляє статичні та динамічні маршрути. Кожна папка в
app/
представляє сегмент маршруту, аpage.tsx
в цих папках відповідає компоненту маршруту. - app/api/: Містить API маршрути, що дозволяє створювати безсерверні функції, які обробляють HTTP запити. Ці маршрути замінюють традиційний каталог
pages/api
. - app/components/: Містить повторно використовувані компоненти React, які можна використовувати на різних сторінках та макетах.
- app/styles/: Містить глобальні CSS файли та CSS модулі для стилізації, обмеженої компонентами.
- app/utils/: Включає утиліти, допоміжні модулі та іншу логіку, не пов'язану з UI, яку можна використовувати в усьому додатку.
- .env.local: Зберігає змінні середовища, специфічні для локального середовища розробки. Ці змінні не комітяться в систему контролю версій.
- next.config.js: Налаштовує поведінку Next.js, включаючи конфігурації webpack, змінні середовища та налаштування безпеки.
- tsconfig.json: Налаштовує параметри TypeScript для проекту, дозволяючи перевірку типів та інші функції TypeScript.
- package.json: Керує залежностями проекту, скриптами та метаданими.
- README.md: Надає документацію та інформацію про проект, включаючи інструкції з налаштування, рекомендації щодо використання та інші відповідні деталі.
- yarn.lock / package-lock.json: Фіксує залежності проекту на конкретних версіях, забезпечуючи послідовні установки в різних середовищах.
Клієнтська сторона в Next.js
Маршрутизація на основі файлів у каталозі app
Каталог app
є основою маршрутизації в останніх версіях Next.js. Він використовує файлову систему для визначення маршрутів, що робить управління маршрутами інтуїтивно зрозумілим та масштабованим.
Обробка кореневого шляху /
Структура файлів:
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
. - Відображення: Цей компонент відображає вміст для сторінки про нас.
Динамічні маршрути
Динамічні маршрути дозволяють обробляти шляхи з змінними сегментами, що дозволяє додаткам відображати вміст на основі параметрів, таких як ID, слуги тощо.
Приклад: маршрут /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 забезпечує безпечну основу, неналежні практики кодування можуть ввести вразливості. Основні вразливості на стороні клієнта включають:
Міжсайтовий скриптинг (XSS)
Атаки XSS відбуваються, коли шкідливі скрипти впроваджуються в довірені веб-сайти. Зловмисники можуть виконувати скрипти в браузерах користувачів, крадучи дані або виконуючи дії від імені користувача.
Приклад вразливого коду:
// Dangerous: Injecting user input directly into HTML
function Comment({ userInput }) {
return <div dangerouslySetInnerHTML={{ __html: userInput }} />
}
Чому це вразливо: Використання dangerouslySetInnerHTML
з ненадійним введенням дозволяє зловмисникам впроваджувати шкідливі скрипти.
Впровадження шаблонів на стороні клієнта
Відбувається, коли введення користувача неправильно обробляється в шаблонах, що дозволяє зловмисникам впроваджувати та виконувати шаблони або вирази.
Приклад вразливого коду:
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
містять шкідливий контент, це може призвести до виконання непередбаченого коду.
Перехід по шляхах клієнта
Це вразливість, яка дозволяє зловмисникам маніпулювати шляхами на стороні клієнта для виконання непередбачених дій, таких як Cross-Site Request Forgery (CSRF). На відміну від переходу по шляхах на стороні сервера, який націлений на файлову систему сервера, CSPT зосереджується на експлуатації механізмів на стороні клієнта для перенаправлення легітимних API запитів на шкідливі кінцеві точки.
Приклад вразливого коду:
Додаток 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>
)
}
Сценарій атаки
- Мета атакуючого: Виконати CSRF-атаку для видалення критичного файлу (наприклад,
admin/config.json
), маніпулюючиfilePath
. - Експлуатація CSPT:
- Зловмисний ввід: Атакуючий створює URL з маніпульованим
filePath
, таким як../deleteFile/config.json
. - Результуючий API виклик: Код на стороні клієнта робить запит до
/api/files/../deleteFile/config.json
. - Обробка сервером: Якщо сервер не перевіряє
filePath
, він обробляє запит, потенційно видаляючи або відкриваючи чутливі файли.
- Виконання CSRF:
- Створене посилання: Атакуючий надсилає жертві посилання або вбудовує зловмисний скрипт, який викликає запит на завантаження з маніпульованим
filePath
. - Результат: Жертва ненавмисно виконує дію, що призводить до несанкціонованого доступу до файлів або їх видалення.
Чому це вразливо
- Відсутність валідації вводу: Код на стороні клієнта дозволяє довільні ввід
filePath
, що дозволяє обходити шляхи. - Довіра до вводу клієнта: API на стороні сервера довіряє та обробляє
filePath
без очищення. - Потенційні дії API: Якщо API-інтерфейс виконує дії, що змінюють стан (наприклад, видалення, модифікація файлів), його можна експлуатувати через CSPT.
Серверна частина в 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
Serverless Functions (API Routes)
Next.js дозволяє створювати API кінцеві точки як безсерверні функції. Ці функції виконуються за запитом без необхідності в виділеному сервері.
Випадки використання:
- Обробка форм.
- Взаємодія з базами даних.
- Обробка даних або інтеграція з API сторонніх розробників.
Впровадження:
З впровадженням директорії app
в Next.js 13, маршрутизація та обробка API стали більш гнучкими та потужними. Цей сучасний підхід тісно пов'язаний з системою маршрутизації на основі файлів, але вводить розширені можливості, включаючи підтримку серверних і клієнтських компонентів.
Basic Route Handler
File Structure:
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" },
})
}
Пояснення:
- Синтаксис:
[...]
позначає сегмент, що охоплює всі вкладені шляхи. - Використання: Корисно для 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
Контролюйте, які джерела можуть отримувати доступ до ваших 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 всередині файлу 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 "server-only"
Ключові файли та їх ролі
middleware.ts
/ middleware.js
Розташування: Корінь проєкту або в src/
.
Призначення: Виконує код у безсерверній функції на стороні сервера перед обробкою запиту, що дозволяє виконувати такі завдання, як аутентифікація, перенаправлення або модифікація відповідей.
Потік виконання:
- Вхідний запит: Проміжне програмне забезпечення перехоплює запит.
- Обробка: Виконує операції на основі запиту (наприклад, перевірка аутентифікації).
- Модифікація відповіді: Може змінювати відповідь або передавати управління наступному обробнику.
Приклади використання:
- Перенаправлення неаутентифікованих користувачів.
- Додавання користувацьких заголовків.
- Логування запитів.
Приклад конфігурації:
// 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
Місцезнаходження: Корінь проекту.
Призначення: Налаштовує поведінку Next.js, увімкнення або вимкнення функцій, налаштування конфігурацій webpack, встановлення змінних середовища та налаштування кількох функцій безпеки.
Ключові конфігурації безпеки:
Заголовки безпеки
Заголовки безпеки підвищують безпеку вашого додатку, інструктуючи браузери, як обробляти контент. Вони допомагають пом'якшити різні атаки, такі як Cross-Site Scripting (XSS), Clickjacking та визначення типу MIME:
- Політика безпеки контенту (CSP)
- X-Frame-Options
- X-Content-Type-Options
- Strict-Transport-Security (HSTS)
- Політика реферера
Приклади:
// 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
},
}
Проблема:
'*'
: Дозволяє завантажувати зображення з будь-якого зовнішнього джерела, включаючи ненадійні або шкідливі домени. Зловмисники можуть розміщувати зображення, що містять шкідливі корисні навантаження або контент, який вводить користувачів в оману.- Іншою проблемою може бути дозволити домен де будь-хто може завантажити зображення (наприклад,
raw.githubusercontent.com
)
Як зловмисники цим зловживають:
Шляхом інжекції зображень з шкідливих джерел, зловмисники можуть здійснювати фішингові атаки, відображати оманливу інформацію або експлуатувати вразливості в бібліотеках рендерингу зображень.
Витік змінних середовища
Керуйте чутливою інформацією, такою як API ключі та облікові дані бази даних, безпечно, не піддаючи їх ризику для клієнта.
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-редиректами та переписуваннями у вашому додатку, забезпечуючи правильне перенаправлення користувачів без введення вразливостей відкритого редиректу.
a. Вразливість відкритого редиректу
Приклад поганої конфігурації:
// next.config.js
module.exports = {
async redirects() {
return [
{
source: "/redirect",
destination: (req) => req.query.url, // Dynamically redirects based on query parameter
permanent: false,
},
]
},
}
Проблема:
- Динамічне призначення: Дозволяє користувачам вказувати будь-яку URL-адресу, що дозволяє атаки з відкритим перенаправленням.
- Довіра до введення користувача: Перенаправлення на URL-адреси, надані користувачами без валідації, може призвести до фішингу, розповсюдження шкідливого ПЗ або крадіжки облікових даних.
Як зловмисники цим зловживають:
Зловмисники можуть створювати 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
},
}
Проблема:
- Викриття чутливих шляхів: Аліасування чутливих директорій та дозволення доступу з боку клієнта може призвести до витоку конфіденційної інформації.
- Упаковка секретів: Якщо чутливі файли упаковані для клієнта, їх вміст стає доступним через карти джерел або перевірку коду з боку клієнта.
Як зловмисники цим зловживають:
Зловмисники можуть отримати доступ або відтворити структуру директорій програми, потенційно знаходячи та експлуатуючи чутливі файли або дані.
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
Мета: Перезаписує стандартний документ, що дозволяє налаштування 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 ключі, у.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_
для клієнтського доступу.
Аутентифікація та авторизація
Підхід:
- Аутентифікація на основі сесій: Використовуйте куки для управління сесіями користувачів.
- Аутентифікація на основі токенів: Реалізуйте JWT для безстанної аутентифікації.
- Постачальники третіх сторін: Інтегруйтеся з постачальниками OAuth (наприклад, Google, GitHub) за допомогою бібліотек, таких як
next-auth
.
Практики безпеки:
- Безпечні куки: Встановіть атрибути
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/image
від Next.js для автоматичної оптимізації зображень. - Розподіл коду: Використовуйте динамічні імпорти для розподілу коду та зменшення початкового часу завантаження.
- Кешування: Реалізуйте стратегії кешування для відповідей API та статичних активів.
- Ліниве завантаження: Завантажуйте компоненти або активи лише тоді, коли вони потрібні.
Приклад:
// Dynamic Import with Code Splitting
import dynamic from "next/dynamic"
const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
loading: () => <p>Loading...</p>,
})
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.