Безопасность Next.js: защита серверных компонентов, CSP, аудит
Безопасность · 13 июня 2026

Безопасность Next.js 16: уязвимости RSC, защита Server Actions, CSP и аудит

Архитектура React Server Components кардинально меняет модель безопасности веб-приложений. Критические уязвимости CVSS 10.0, новая поверхность атаки Server Actions, CSP, аудит зависимостей и защита деплоя — всё, что нужно знать разработчику Next.js для продакшен-безопасности.

Олег Максимов 13 июня 2026 25 мин чтения

⚠ Критически важно

Если вы используете Next.js 15.x или 16.x с App Router, ваше приложение может быть уязвимо для RCE без аутентификации (CVE-2025-66478, CVSS 10.0). Проверьте версию Next.js и установите патч, если не сделали этого ранее.

Кратко: что должен сделать каждый разработчик Next.js

1. React Server Components: поверхность атаки и исправления

React Server Components (RSC) — ключевое архитектурное изменение в Next.js, которое переносит рендеринг и доступ к данным на сервер. Это радикально улучшает производительность, но создаёт новую серверную поверхность атаки. В декабре 2025 — январе 2026 годов были раскрыты несколько критических уязвимостей, затрагивающих стек RSC.

1.1 Протокольные уязвимости (семейство «React2Shell»)

CVE-2025-66478 (CVSS 10.0) — уязвимость протокола RSC, позволяющая удалённое выполнение кода (RCE) без аутентификации через специально сформированные HTTP-запросы. Происходит из upstream React (CVE-2025-55182). Затрагивает Next.js 15.x, 16.x, а также 14.3.0-canary.77+. Не затрагивает 13.x, 14.x stable, Pages Router и Edge Runtime.

Исправлена в версиях: 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7. Если ваше приложение оставалось неисправленным по состоянию на 4 декабря 2025 года 13:00 PT — незамедлительно смените все секреты.

CVE-2025-55184 (High, CVSS 7.5) — DoS через специально сформированный HTTP-запрос, вызывающий бесконечный цикл. Любой App Router endpoint может быть целью. Первоначальное исправление было неполным; окончательное — под CVE-2025-67779.

CVE-2025-55183 (Medium, CVSS 5.3) — утечка исходного кода Server Functions. Сконструированный запрос может вернуть скомпилированный исходный код других Server Functions. Риск: утечка бизнес-логики и секретов, встроенных в скомпилированный вывод (не переменные окружения).

Таблица версий (уязвимости RSC)

Версия CVE-2025-66478 (RCE) CVE-2025-55184 (DoS) CVE-2025-55183 (Утечка) Исправлено в
13.3+ 14.2.35
14.x 14.2.35
15.0.x 15.0.7
15.1.x 15.1.11
15.2.x 15.2.8
15.3.x 15.3.8
15.4.x 15.4.10
15.5.x 15.5.9
16.0.x 16.0.7

1.2 Утечка данных через архитектуру RSC

Server Components могут напрямую обращаться к базам данных, файловой системе и секретам — но могут непреднамеренно утекать данные через пропсы, передаваемые Client Components. Основные риски:

Решения: Data Access Layer (DAL) с директивами server-only и React Taint API (experimental_taintObjectReference, experimental_taintUniqueValue), включаемый через experimental: { taint: true } в next.config.js.

1.3 Предотвращение утечки серверного кода

// server-only — вызовет ошибку сборки при импорте из Client Component
import 'server-only'

export function getAuthorizedData(userId: string) {
  // Этот код гарантированно не попадёт в клиентский бандл
}

Используйте import 'server-only' для принудительной изоляции серверных модулей. Переменные окружения: process.env доступен только на сервере; префикс NEXT_PUBLIC_ экспортирует значение в клиентский бандл.

2. Безопасность Server Actions: аутентификация, авторизация и CSRF

2.1 Три незыблемых правила

  1. Server Actions доступны через прямые POST-запросы — не только через UI. Любой может отправить запрос на endpoint Server Action.
  2. Всегда проверяйте аутентификацию и авторизацию внутри каждой Server Action — проверки на странице не защищают endpoint.
  3. Page-level проверки не защищают Server Actions, определённые на этой же странице.
// НЕПРАВИЛЬНО: проверка только в UI
export default function DeletePost({ postId }: { postId: string }) {
  // ... пользователь видит кнопку удаления
  // Server Action ниже — НЕ ЗАЩИЩЁН
  async function deletePost() {
    'use server'
    await db.post.delete({ where: { id: postId } })
  }
}

// ПРАВИЛЬНО: проверка внутри Server Action
export default function DeletePost({ postId }: { postId: string }) {
  async function deletePost(formData: FormData) {
    'use server'
    const session = await getSession()
    if (!session?.user) throw new Error('Не авторизован')

    const post = await db.post.findUnique({ where: { id: postId } })
    if (!post || post.authorId !== session.user.id) {
      throw new Error('Нет прав на удаление')
    }
    await db.post.delete({ where: { id: postId } })
  }
}

2.2 Встроенная защита Server Actions

2.3 Предотвращение IDOR

Insecure Direct Object Reference — уязвимость, когда пользователь может получить доступ к чужому ресурсу, подменив ID в запросе. Server Actions особенно уязвимы, так как параметры (ID поста, ID пользователя) приходят от клиента.

// Всегда проверяйте принадлежность ресурса
async function updatePost(formData: FormData) {
  'use server'
  const session = await getSession()
  const post = await db.post.findUnique({ where: { id: formData.get('postId') } })

  // Проверка: принадлежит ли пост текущему пользователю?
  if (post.authorId !== session.user.id) {
    throw new Error('У вас нет прав на редактирование этого поста')
  }

  // Только после проверки — выполнение мутации
  await db.post.update({ where: { id: post.id }, data: { title: formData.get('title') } })
}

2.4 Шифрование замыканий (Closure Encryption)

Server Actions, определённые внутри компонентов, создают замыкания с захваченными переменными. Next.js автоматически шифрует эти переменные. Ключ шифрования генерируется автоматически при каждой сборке; для self-hosted можно переопределить через NEXT_SERVER_ACTIONS_ENCRYPTION_KEY.

Важно: не полагайтесь на шифрование замыканий как на единственную защиту для чувствительных значений. Ключи ротации — стандартная практика для self-hosted деплоев.

2.5 Allowed Origins (продвинутая CSRF-защита)

Server Actions сравнивают Origin с Host (или X-Forwarded-Host) для защиты от CSRF. Для multi-layered бэкендов с обратными прокси настройте serverActions.allowedOrigins в next.config.js.

2.6 Валидация входных данных и контроль возвращаемых значений

Никогда не доверяйте searchParams, form data или URL-параметрам без валидации. Используйте схемы валидации (Zod, Yup) внутри Server Actions. DTO-паттерн: возвращайте только то, что нужно UI, а не сырые записи БД.

import { z } from 'zod'

const postSchema = z.object({
  title: z.string().min(3).max(200),
  content: z.string().min(10),
})

async function createPost(formData: FormData) {
  'use server'
  const data = postSchema.parse({
    title: formData.get('title'),
    content: formData.get('content'),
  })
  // data — безопасный, проверенный объект
  const post = await db.post.create({ data })
  return { success: true, id: post.id } // DTO — не сырая запись
}

2.7 Rate Limiting

Server Actions, выполняющие дорогие операции (отправка email, запись в БД), должны иметь ограничение частоты запросов. Используйте Backend for Frontend (BFF) паттерн с rate limiting на уровне обратного прокси или middleware.

3. Content Security Policy: nonce, SRI и конфигурация

3.1 Nonce-Based CSP (строгий режим)

Требует динамического рендеринга всех страниц — все страницы должны быть SSR, без статики/CDN. Компромисс: нет кэширования CDN, выше нагрузка на сервер, несовместимо с ISR/PPR.

// proxy.ts (бывший middleware.ts)
import { NextRequest, NextResponse } from 'next/server'

export function middleware(request: NextRequest) {
  const nonce = crypto.randomUUID()
  const csp = [
    `default-src 'self'`,
    `script-src 'nonce-${nonce}' 'strict-dynamic'`,
    `style-src 'nonce-${nonce}' 'unsafe-inline'`,
    `img-src 'self' data: blob: https:`,
    `base-uri 'none'`,
  ].join('; ')

  const response = NextResponse.next()
  response.headers.set('Content-Security-Policy', csp)
  response.headers.set('x-nonce', nonce)
  return response
}

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon).*)'],
}

3.2 Hash-Based CSP с SRI (экспериментальный)

Альтернатива nonce — поддерживает статическую генерацию и кэширование CDN. Хэши генерируются на этапе сборки через experimental.sri.algorithm. Ограничения: экспериментальная функция, только App Router, не работает с динамическими скриптами.

3.3 Статический CSP без nonce

Для приложений без строгих требований CSP — установите заголовки через async headers() в next.config.js. Позволяет 'unsafe-inline' для скриптов и стилей — проще, но менее безопасно. Поддерживает статическую генерацию и CDN.

3.4 Development vs Production CSP

В development режиме требуется 'unsafe-eval' — React использует eval для стек-трейсов. В production eval не нужен. Используйте условный шаблон:

const isDev = process.env.NODE_ENV === 'development'
const csp = `script-src 'nonce-${nonce}' 'strict-dynamic' ${isDev ? "'unsafe-eval'" : ''}`

4. Аудит зависимостей и безопасность цепочки поставок

4.1 Автоматическое сканирование зависимостей

4.2 Риски, специфичные для экосистемы Next.js

4.3 Чеклист аудита безопасности Next.js

4.4 Инструменты для аудита

Важно: безопасность npm-пакетов — отдельная большая тема. Уязвимость CVE-2026-41242 в protobuf.js (CVSS 9.8) — отличный пример того, как одна несанитизированная интерполяция строк скомпрометировала миллионы приложений. Используйте чеклист оценки npm пакета перед установкой для предотвращения таких инцидентов.

5. Безопасность деплоя: заголовки, прокси и окружение

5.1 Security Headers

Настройте через async headers() в next.config.js:

// next.config.js
const securityHeaders = [
  { key: 'X-Content-Type-Options', value: 'nosniff' },
  { key: 'X-Frame-Options', value: 'DENY' },
  { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
  { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
  { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
]

module.exports = {
  async headers() {
    return [
      { source: '/(.*)', headers: securityHeaders },
    ]
  },
}

5.2 Безопасность Proxy (Middleware)

Proxy выполняется перед каждым запросом — мощный, но рискованный инструмент. Ключевое различие: NextResponse.next({ request: { headers } }) модифицирует внутренние заголовки, а New Response(..., { headers }) отправляет заголовки клиенту — чувствительные данные могут утечь.

5.3 Self-Hosting: архитектура обратного прокси

Никогда не暴露йте Next.js сервер напрямую в интернет. Используйте nginx, Caddy или Traefik как обратный прокси для обработки malformed-запросов, slow connection attacks, rate limiting, ограничения размера payload и TLS-терминации.

# Пример nginx reverse proxy
server {
    listen 443 ssl;
    server_name my-nextjs-app.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
    }
}

5.4 Безопасность Image Optimization

5.5 Безопасность переменных окружения

6. Архитектурные паттерны: Defense-in-Depth

6.1 Data Access Layer (DAL)

Централизованный, server-only слой доступа к данным с обязательной проверкой авторизации. Возвращает минимальные DTO — никогда не сырые записи БД. Использует React.cache() для разделяемого кэширования.

import 'server-only'
import { cache } from 'react'

export const getProfileDTO = cache(async (userId: string) => {
  const session = await getSession()
  if (!session?.user) throw new Error('Unauthorized')

  const user = await db.user.findUnique({ where: { id: userId } })
  if (!user) return null

  // DTO — только то, что нужно UI
  return {
    name: user.name,
    email: user.email,
    avatar: user.avatarUrl,
    role: user.role,
  }
})

6.2 Zero Trust в контексте RSC

Относитесь к любому доступу Server Component к данным как к недоверенному по умолчанию. Не предполагайте безопасность внутренней сети — проверяйте на каждом уровне. Каждый модуль DAL должен проверять авторизацию независимо.

6.3 Предотвращение side-effects во время рендеринга

Никогда не выполняйте мутации (logout, запись в БД, инвалидация кэша) во время рендеринга. Next.js явно предотвращает cookies/cache revalidation внутри render-методов. Используйте Server Actions для мутаций через отправку формы.

6.4 Многоуровневая CSRF-защита

  1. SameSite cookies (по умолчанию в современных браузерах)
  2. Сравнение Origin/Host в Server Actions
  3. serverActions.allowedOrigins для reverse proxy
  4. Отправка форм только через POST (Server Actions используют POST)

6.5 SSR/Streaming: соображения безопасности

При стриминге частичный контент может утечь, если проверка авторизации отложена. Используйте connection() API для принудительного ожидания входящего запроса перед рендерингом. Балансируйте между Cache Components и Dynamic rendering в зависимости от требований безопасности.

7. Чеклист продакшен-безопасности Next.js

Финальный чеклист — пройдите перед деплоем или после любого обновления:

FAQ

Какие критические уязвимости RSC существуют в Next.js?
На данный момент зафиксированы три основные уязвимости: CVE-2025-66478 (RCE, CVSS 10.0 — критическая уязвимость протокола RSC), CVE-2025-55184 (DoS, CVSS 7.5 — отказ в обслуживании через специально сформированный HTTP-запрос) и CVE-2025-55183 (утечка исходного кода Server Functions, CVSS 5.3). Все три исправлены в Next.js 15.0.7+ и 16.0.7+.
Нужно ли проверять авторизацию внутри каждой Server Action?
Да, обязательно. Server Actions доступны через прямые POST-запросы, минуя клиентский интерфейс. Проверки на странице (например, скрытие кнопки для неавторизованных пользователей) не защищают Server Actions. Всегда проверяйте аутентификацию и авторизацию непосредственно внутри каждой Server Action, используя DTO-паттерн для ограничения возвращаемых данных.
Какой тип CSP выбрать для Next.js — nonce или SRI?
Nonce-based CSP обеспечивает максимальную защиту, но требует динамического рендеринга всех страниц (нет CDN-кэширования, ISR или PPR). Hash-based CSP с SRI (experimental.sri.algorithm) поддерживает статическую генерацию и кэширование, но не работает с динамическими скриптами. Выбор зависит от архитектуры: для SSR-приложений с высокой нагрузкой — nonce, для статических сайтов — SRI.
Как защититься от IDOR в Server Actions?
Всегда проверяйте принадлежность ресурса внутри Server Action, а не полагайтесь на ID из запроса. Паттерн: получите запись из БД, сравните authorId с session.user.id, только при совпадении выполняйте мутацию. Используйте Data Access Layer (DAL) с обязательной проверкой прав доступа перед каждым запросом к данным.
Нужен ли обратный прокси для Next.js в продакшене?
Да, категорически рекомендуется. Никогда не expose Next.js сервер напрямую в интернет. Используйте nginx, Caddy или Traefik как обратный прокси для обработки malformed-запросов, rate limiting, ограничения размера payload и TLS-терминации. Next.js server должен слушать только на localhost.
Что такое Data Access Layer (DAL) в Next.js?
DAL — это централизованный слой доступа к данным, изолированный через server-only. Он выполняет проверку авторизации, возвращает минимальные DTO вместо полных записей БД и использует React.cache() для разделяемого кэширования в памяти. Все Server Components и Server Actions обращаются к данным только через DAL.
Как настроить проверку Origin/Host в Server Actions?
Next.js автоматически сравнивает Origin и Host заголовки для всех Server Actions. Если они не совпадают, запрос отклоняется как потенциальный CSRF. Для multi-layered бэкендов с обратным прокси настройте serverActions.allowedOrigins в next.config.js, указав разрешённые домены.

Заключение

Безопасность Next.js 16 — это многослойная задача. Уязвимости React Server Components (CVE-2025-66478, CVSS 10.0) показали, что новая архитектура требует нового подхода к безопасности. Server Actions меняют модель аутентификации — проверки на странице больше не работают, авторизация должна быть внутри каждой action. Content Security Policy с nonce или SRI защищает от XSS и code injection. Аудит зависимостей и безопасность цепочки поставок становятся обязательным элементом CI/CD.

Хорошая новость в том, что Next.js предоставляет все необходимые инструменты: server-only, React Taint API, встроенную CSRF-защиту, гибкую конфигурацию CSP и security headers. Ключ к безопасности — применение всех этих инструментов как единой системы, а не выборочно.

Архитектура Defense-in-Depth с DAL, zero trust, многоуровневой CSRF-защитой и правильной конфигурацией деплоя — единственный подход, который работает в современных условиях, когда уязвимости эксплуатируются в течение часов после раскрытия.

Контакты

Нужна помощь с вашим Next.js проектом?

Я разрабатываю production-приложения на Next.js, React и Node.js. Обсудим безопасность и архитектуру вашего проекта.