Стандартний проект Next.js дотримується специфічної структури файлів і каталогів, що полегшує його функції, такі як маршрутизація, API-інтерфейси та управління статичними активами. Ось типове розташування:
public/: Зберігає статичні ресурси, такі як зображення, шрифти та інші файли. Файли тут доступні за кореневим шляхом (/).
app/: Центральний каталог для сторінок, макетів, компонентів та API маршрутів вашого додатку. Впроваджує парадигму App Router, що дозволяє використовувати розширені функції маршрутизації та сегрегацію компонентів серверу та клієнту.
app/layout.tsx: Визначає кореневий макет для вашого додатку, обгортаючи всі сторінки та надаючи послідовні елементи інтерфейсу, такі як заголовки, підвал та навігаційні панелі.
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/: Включає утиліти, допоміжні модулі та іншу логіку, не пов'язану з інтерфейсом, яку можна використовувати в усьому додатку.
.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. Він використовує файлову систему для визначення маршрутів, що робить управління маршрутами інтуїтивно зрозумілим та масштабованим.
// app/about/page.tsxexportdefaultfunctionAboutPage() {return (<div><h1>About Us</h1><p>Learn more about our mission and values.</p></div>);}
Пояснення:
Визначення маршруту: Файл page.tsx всередині папки about відповідає маршруту /about.
Відображення: Цей компонент відображає вміст для сторінки про нас.
Динамічні маршрути
Динамічні маршрути дозволяють обробляти шляхи з змінними сегментами, що дозволяє додаткам відображати вміст на основі параметрів, таких як ID, слуги тощо.
tsxCopy code// app/posts/[id]/page.tsximport { useRouter } from'next/navigation';interfacePostProps {params: { id:string };}exportdefaultfunctionPostPage({ 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 підтримує вкладене маршрутизування, що дозволяє створювати ієрархічні структури маршрутів, які відображають структуру директорій.
tsxCopy code// app/dashboard/settings/profile/page.tsxexportdefaultfunctionProfileSettingsPage() {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" обробляють кілька вкладених сегментів або невідомі шляхи, забезпечуючи гнучкість в обробці маршрутів.
// app/[...slug]/page.tsxinterfaceCatchAllProps {params: { slug:string[] };}exportdefaultfunctionCatchAllPage({ params }:CatchAllProps) {const { slug } = params;constfullPath=`/${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 HTMLfunctionComment({ userInput }) {return <divdangerouslySetInnerHTML={{ __html: userInput }} />;}
Чому це вразливо: Використання dangerouslySetInnerHTML з ненадійним введенням дозволяє зловмисникам впроваджувати шкідливі скрипти.
Впровадження шаблонів на стороні клієнта
Відбувається, коли введення користувачів неправильно обробляється в шаблонах, що дозволяє зловмисникам впроваджувати та виконувати шаблони або вирази.
Приклад вразливого коду:
import React from'react';import ejs from'ejs';functionRenderTemplate({ template, data }) {consthtml=ejs.render(template, data);return <divdangerouslySetInnerHTML={{ __html: html }} />;}
Чому це вразливо: Якщо template або data містять шкідливий контент, це може призвести до виконання непередбаченого коду.
Перехід по шляху клієнта
Це вразливість, яка дозволяє зловмисникам маніпулювати шляхами на стороні клієнта для виконання непередбачених дій, таких як Cross-Site Request Forgery (CSRF). На відміну від переходу по шляху на стороні сервера, який націлений на файлову систему сервера, CSPT зосереджується на експлуатації механізмів на стороні клієнта для перенаправлення легітимних API запитів на шкідливі кінцеві точки.
Приклад вразливого коду:
Додаток Next.js дозволяє користувачам завантажувати та скачувати файли. Функція завантаження реалізована на стороні клієнта, де користувачі можуть вказати шлях до файлу для завантаження.
Мета атакуючого: Виконати 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.jsexportasyncfunctiongetServerSideProps(context) {constres=awaitfetch('https://api.example.com/data');constdata=awaitres.json();return { props: { data } };}functionHomePage({ data }) {return <div>{data.title}</div>;}exportdefault HomePage;
Static Site Generation (SSG)
Сторінки попередньо рендеряться під час збірки, що призводить до швидшого часу завантаження та зменшення навантаження на сервер.
Use Cases:
Контент, який не змінюється часто.
Блоги, документація, маркетингові сторінки.
Implementation:
// pages/index.jsexportasyncfunctiongetStaticProps() {constres=awaitfetch('https://api.example.com/data');constdata=awaitres.json();return { props: { data }, revalidate:60 }; // Revalidate every 60 seconds}functionHomePage({ data }) {return <div>{data.title}</div>;}exportdefault HomePage;
Serverless Functions (API Routes)
Next.js дозволяє створювати API кінцеві точки як безсерверні функції. Ці функції виконуються за запитом без необхідності в спеціалізованому сервері.
Випадки використання:
Обробка відправлень форм.
Взаємодія з базами даних.
Обробка даних або інтеграція з сторонніми API.
Впровадження:
З впровадженням директорії app в Next.js 13, маршрутизація та обробка API стали більш гнучкими та потужними. Цей сучасний підхід тісно пов'язаний з системою маршрутизації на основі файлів, але вводить розширені можливості, включаючи підтримку серверних і клієнтських компонентів.
// app/api/hello/route.jsexportasyncfunctionPOST(request) {returnnewResponse(JSON.stringify({ message:'Hello from App Router!' }), {status:200,headers: { 'Content-Type':'application/json' },});}// Client-side fetch to access the API endpointfetch('/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.jsexportasyncfunctionGET(request, { params }) {const { id } = params;// Fetch user data based on 'id'returnnewResponse(JSON.stringify({ userId: id, name:'Jane Doe' }), {status:200,headers: { 'Content-Type':'application/json' },});}exportasyncfunctionPUT(request, { params }) {const { id } = params;// Update user data based on 'id'returnnewResponse(JSON.stringify({ message:`User ${id} updated.` }), {status:200,headers: { 'Content-Type':'application/json' },});}exportasyncfunctionDELETE(request, { params }) {const { id } = params;// Delete user based on 'id'returnnewResponse(JSON.stringify({ message:`User ${id} deleted.` }), {status:200,headers: { 'Content-Type':'application/json' },});}
Пояснення:
Багато експортів: Кожен HTTP метод (GET, PUT, DELETE) має свою власну експортовану функцію.
Параметри: Другий аргумент надає доступ до параметрів маршруту через params.
Покращені відповіді: Більший контроль над об'єктами відповіді, що дозволяє точно керувати заголовками та кодами статусу.
Універсальні та вкладені маршрути
Next.js 13+ підтримує розширені функції маршрутизації, такі як універсальні маршрути та вкладені API маршрути, що дозволяє створювати більш динамічні та масштабовані структури API.
Синтаксис:[...] позначає сегмент, що охоплює всі вкладені шляхи.
Використання: Корисно для API, які повинні обробляти різну глибину маршрутів або динамічні сегменти.
Приклад вкладених маршрутів:
// app/api/posts/[postId]/comments/[commentId]/route.jsexportasyncfunctionGET(request, { params }) {const { postId,commentId } = params;// Fetch specific comment for a postreturnnewResponse(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 та раніших версіях.
javascriptCopy code// pages/api/users/[id].jsexportdefaultfunctionhandler(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`);}}
Доступ до параметрів: Використовуйте req.query.id для доступу до динамічного параметра.
Обробка методів: Використовуйте умовну логіку для обробки різних HTTP-методів (GET, PUT, DELETE тощо).
Обробка різних HTTP-методів
Хоча базовий приклад API-маршруту обробляє всі HTTP-методи в одній функції, ви можете структурувати свій код для явної обробки кожного методу для кращої ясності та підтримуваності.
Приклад:
javascriptCopy code// pages/api/posts.jsexportdefaultasyncfunctionhandler(req, res) {const { method } = req;switch (method) {case'GET':// Handle GET requestres.status(200).json({ message:'Fetching posts.' });break;case'POST':// Handle POST requestres.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.jsexportasyncfunctionGET(request) {returnnewResponse(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.tsimport { NextResponse } from'next/server';importtype { NextRequest } from'next/server';exportfunctionmiddleware(request:NextRequest) {constallowedOrigins= ['https://yourdomain.com','https://sub.yourdomain.com'];constorigin=request.headers.get('Origin');constresponse=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 requestsif (request.method ==='OPTIONS') {returnnewResponse(null, {status:204,headers:response.headers,});}return response;}exportconstconfig= {matcher:'/api/:path*',// Apply to all API routes};
Проблема:
Access-Control-Allow-Origin: '*': Дозволяє будь-якому веб-сайту отримувати доступ до API, що потенційно дозволяє шкідливим сайтам взаємодіяти з вашим API без обмежень.
Широке дозволення методів: Дозволення всіх методів може дозволити зловмисникам виконувати небажані дії.
Як зловмисники це експлуатують:
Зловмисники можуть створювати шкідливі веб-сайти, які надсилають запити до вашого API, потенційно зловживаючи функціональністю, такою як отримання даних, маніпуляція даними або ініціювання небажаних дій від імені автентифікованих користувачів.
Легко використовувати код, що використовується сервером, також у коді, що відкритий і використовується на стороні клієнта, найкращий спосіб забезпечити, щоб файл коду ніколи не був відкритий на стороні клієнта, це використання цього імпорту на початку файлу:
import"server-only";
Ключові файли та їх ролі
middleware.ts / middleware.js
Розташування: Корінь проєкту або в src/.
Призначення: Виконує код у безсерверній функції на стороні сервера перед обробкою запиту, що дозволяє виконувати такі завдання, як аутентифікація, перенаправлення або модифікація відповідей.
Потік виконання:
Вхідний запит: Проміжне програмне забезпечення перехоплює запит.
Обробка: Виконує операції на основі запиту (наприклад, перевірка аутентифікації).
Модифікація відповіді: Може змінювати відповідь або передавати управління наступному обробнику.
Призначення: Налаштовує поведінку Next.js, увімкнення або вимкнення функцій, налаштування конфігурацій webpack, встановлення змінних середовища та налаштування кількох функцій безпеки.
Ключові конфігурації безпеки:
Заголовки безпеки
Заголовки безпеки підвищують безпеку вашого додатку, інструктуючи браузери, як обробляти контент. Вони допомагають пом'якшити різні атаки, такі як Cross-Site Scripting (XSS), Clickjacking та визначення типу MIME:
Next.js оптимізує зображення для продуктивності, але неправильні налаштування можуть призвести до вразливостей безпеки, таких як дозволення ненадійним джерелам впроваджувати шкідливий контент.
Приклад поганого налаштування:
// next.config.jsmodule.exports= {images: {domains: ['*'],// Allows images from any domain},};
Проблема:
'*': Дозволяє завантажувати зображення з будь-якого зовнішнього джерела, включаючи ненадійні або шкідливі домени. Зловмисники можуть розміщувати зображення, що містять шкідливі дані або контент, який вводить користувачів в оману.
Іншою проблемою може бути дозволити домен куди будь-хто може завантажити зображення (наприклад, raw.githubusercontent.com)
Як зловмисники цим зловживають:
Шляхом інжекції зображень з шкідливих джерел, зловмисники можуть здійснювати фішингові атаки, відображати оманливу інформацію або експлуатувати вразливості в бібліотеках рендерингу зображень.
Витік змінних середовища
Керуйте чутливою інформацією, такою як API ключі та облікові дані бази даних, безпечно, не піддаючи їх ризику для клієнта.
a. Витік чутливих змінних
Приклад поганої конфігурації:
// next.config.jsmodule.exports= {env: {SECRET_API_KEY:process.env.SECRET_API_KEY,// Exposed to the clientNEXT_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.jsmodule.exports= {asyncredirects() {return [{source:'/redirect',destination: (req) =>req.query.url,// Dynamically redirects based on query parameterpermanent:false,},];},};
Проблема:
Динамічний напрямок: Дозволяє користувачам вказувати будь-яку URL-адресу, що дозволяє атаки з відкритим перенаправленням.
Довіра до введення користувача: Перенаправлення на URL-адреси, надані користувачами без валідації, може призвести до фішингу, розповсюдження шкідливого ПЗ або крадіжки облікових даних.
Як зловмисники це використовують:
Зловмисники можуть створювати URL-адреси, які, здається, походять з вашого домену, але перенаправляють користувачів на шкідливі сайти. Наприклад:
Мета: Хоча Next.js постачається з вбудованим сервером, ви можете створити власний сервер для розширених випадків використання, таких як налаштоване маршрутизування або інтеграція з існуючими бекенд-сервісами.
Примітка: Використання власного сервера може обмежити варіанти розгортання, особливо на платформах, таких як Vercel, які оптимізують вбудований сервер Next.js.
Мета: Керувати чутливою інформацією та налаштуваннями конфігурації поза межами кодової бази.
Найкращі практики:
Використовуйте .env файли: Зберігайте змінні, такі як API ключі, у .env.local (виключено з контролю версій).
Безпечно отримуйте змінні: Використовуйте process.env.VARIABLE_NAME для доступу до змінних середовища.
Ніколи не розкривайте секрети на клієнті: Переконайтеся, що чутливі змінні використовуються лише на стороні сервера.
Приклад:
// next.config.jsmodule.exports= {env: {API_KEY:process.env.API_KEY,// Accessible on both client and serverSECRET_KEY:process.env.SECRET_KEY,// Be cautious if accessible on the client},};
Примітка: Щоб обмежити змінні лише для серверної частини, виключіть їх з об'єкта env або додайте префікс NEXT_PUBLIC_ для клієнтського доступу.
Аутентифікація та авторизація
Підхід:
Аутентифікація на основі сесій: Використовуйте куки для управління сесіями користувачів.
Аутентифікація на основі токенів: Реалізуйте JWT для безстанної аутентифікації.
Постачальники третіх сторін: Інтегруйтеся з постачальниками OAuth (наприклад, Google, GitHub) за допомогою бібліотек, таких як next-auth.
Практики безпеки:
Безпечні куки: Встановіть атрибути HttpOnly, Secure та SameSite.
Хешування паролів: Завжди хешуйте паролі перед їх зберіганням.
Валідація введення: Запобігайте атакам ін'єкцій, валідувавши та очищаючи введення.