Уязвимость React RCE (CVE-2025-55182) – проблема и причины возникновения
В начале декабря 2025 года была раскрыта уязвимость в React Server Components (CVE-2025-55182, также известная как React2Shell / React4Shell), оцененная CVSS 10.0. Это крайне серьёзная проблема, позволяющая выполнить удалённый код (RCE) без предварительной аутентификации. Уже наблюдаются публичные PoC и реальные попытки сканирования и атаки.
В этой статье мы более систематически разберём «кто, почему и насколько опасно».
Быстрый обзор
-
Суть проблемы Уязвимость в логике десериализации протокола Flight, используемого React Server Components (RSC), позволяет злоумышленнику отправить модифицированный запрос, после чего сервер выполнит произвольный JS‑код.
-
Основные затронутые компоненты
react-server-dom-webpack / -parcel / -turbopack19.0 / 19.1.0 / 19.1.1 / 19.2.0-
Встроенные в них Next.js 15.x, 16.x, 14.3.0‑canary.77 и новее (при использовании App Router)
-
Только браузерные «традиционные» React SPA Приложения, которые не используют RSC/Server Actions/серверный рендеринг, не затронуты.
-
Что делать, если вы раньше использовали Next.js 1. Проверьте версии
react-server-dom-*иnextв проекте. 2. Обновите React до 19.0.1 / 19.1.2 / 19.2.1 или новее. 3. Обновите Next.js до объявленных патч‑версий (15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7 и т.д.) или откатитесь на стабильную версию. 4. Если сервер уже открыт в интернете, проверьте логи, правила WAF и наличие подозрительных событий.

Что именно было найдено
Согласно информации, опубликованной командой React, уязвимость можно резюмировать так:
- Цель: Реализация React Server Components (
react-server-dom-*). - Уязвимый участок: Логика декодирования (десериализации) протокола Flight, используемая при передаче данных RSC от клиента к серверу.
- Метод атаки:
- Злоумышленник отправляет специально сформированный Flight‑payload на HTTP‑эндпоинт RSC/Server Actions.
- Сервер «доверяет» этим данным и, десериализуя их, позволяет внутренним модулям загружать объекты, управляемые вводом злоумышленника.
- В результате возможна выполнение произвольного кода на сервере (RCE).
Ключевой момент: атака возможна без логина/сессии (Unauthenticated). Любой Next.js RSC сервер, открытый в интернете, становится потенциальной целью с момента его публикации.
Точные условия возникновения уязвимости
1. Условия версии React/Next.js
- Пакеты, связанные с RSC
react-server-dom-webpackreact-server-dom-parcel-
react-server-dom-turbopack -
Уязвимые версии
-
19.0, 19.1.0, 19.1.1, 19.2.0
-
Патч‑версии
-
19.0.1, 19.1.2, 19.2.1
-
Next.js (App Router)
- Уязвимые: 14.3.0‑canary.77 и новее, 15.x, 16.x (с базовой структурой App Router, включающей RSC)
- Патч‑версии: 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7 и т.д.
2. Архитектурные/рантайм‑условия
Атака возможна, если соблюдены все следующие условия:
- Используется RSC * Next.js App Router или другой фреймворк, поддерживающий RSC.
- На сервере исполняется React‑код * Node.js и т.д. запускают RSC/Server Actions.
- Сервер доступен по HTTP извне * Интернет или внутренний сетевой доступ, позволяющий отправлять HTTP‑запросы.
Обратные случаи почти не затрагиваются:
- CRA/Vite‑базированные чисто клиентские React SPA.
- Публикация только статических бандлов в S3, CDN и т.п.
- Структуры, где React работает только в браузере, а сервер – полностью отдельный стек (например, React + Django REST, React + FastAPI).
Почему сочетание React‑DRF и React‑FastAPI оказалось относительно безопасным
В структурах, где React используется совместно с Django REST Framework или FastAPI, проблема не проявляется. Это связано с тем, что:
- React не исполняется на сервере * React работает только в браузере, а Django/FastAPI – чистый HTTP‑API сервер. * Сервер не понимает протокол Flight и не обрабатывает RSC.
- Чёткое разделение коммуникаций * Связь React ↔ сервер осуществляется через JSON/HTTP API. * Сервер не загружает модули React по
require()или на основеmetadataот клиента. - Отсутствие RSC/Server Actions/Flight * Эти технологии не используются, поэтому уязвимый путь отсутствует.
Таким образом, уязвимость возникла только в структурах, где React и сервер тесно интегрированы (Next.js и подобные). В разделённых архитектурах (React‑SPA + REST API) проблемный код не существует.
Протокол Flight и прототип‑заполнения на стороне сервера
Технически уязвимость следует типичному шаблону: десериализация протокола Flight → прототип‑заполнение → RCE.
1. Паттерн проблемного кода
// Уязвимая версия (концептуальный код)
export function requireModule<T>(metadata: ClientReference<T>): T {
const moduleExports = parcelRequire(metadata[ID]); // загрузка модуля
return moduleExports[metadata[NAME]]; // прямое использование клиентского ввода
}
metadata[ID]: какой модуль загружатьmetadata[NAME]: какой экспорт из модуля использовать
Проблема в том, что metadata[NAME] используется без проверки. Если злоумышленник подставит __proto__, constructor и т.п., он может изменить цепочку прототипов объекта.
2. Прототип‑заполнение → RCE
- Где‑то в серверном коде создаётся «обычный» объект.
- Если
Object.prototypeуже загрязнён, новые свойства попадают в объект. - Если в цепочке есть логика, запускающая
spawnSync('sh')или аналог, код выполнится. - Таким образом, один HTTP‑запрос может привести к удалённому выполнению кода.
Что именно было исправлено
Ключевая часть патча – принудительное не‑доверие клиентских данных. Основное изменение – в функции requireModule.
// После патча (концептуальный код)
export function requireModule<T>(metadata: ClientReference<T>): T {
const moduleExports = parcelRequire(metadata[ID]);
// 👇 Проверяем, что NAME является собственным свойством модуля
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
return moduleExports[metadata[NAME]];
}
return undefined as any;
}
Эта строка блокирует доступ к __proto__, constructor и другим ключам, которые не являются собственными свойствами модуля, тем самым закрывая основной путь к прототип‑заполнению.
Патч также включал:
- Усиленную проверку структуры Flight‑payload
- Ограничение использования опасных полей
- Дополнительные защитные механизмы в связанных кодовых путях
Как возникло структурное противоречие при расширении React на сервер
Изначально React был «только» библиотекой для браузера. С появлением RSC и Server Actions он перестал быть исключительно клиентским.
Next.js интегрировал эти возможности, создавая единую среду, где:
- Клиентские и серверные компоненты, а также серверные действия выглядят как единое приложение.
- Данные, отправляемые клиентом, напрямую влияют на загрузку серверных модулей и выполнение кода.
Это нарушило модель безопасности, основанную на предположении, что фронтенд не имеет доступа к серверным ресурсам.
Проблема переноса фронтенд‑предположений на сервер
В фронтенде:
- Бандлы статичны и неизменны.
- Загрузка модулей фиксирована на этапе сборки.
- Пользовательский ввод редко меняет имя модуля или экспорт.
На сервере:
- Ввод всегда ненадёжный.
- Загрузка модулей и создание объектов напрямую связаны с ресурсами сервера.
- Любое динамическое использование пользовательского ввода открывает путь к RCE.
Нужна была новая модель безопасности:
- Строгая схема проверки Flight‑payload.
- Белый список допустимых значений, контролируемых клиентом.
- Подпись/проверка целостности запросов (HMAC и т.п.).
- Предположение, что RSC всегда получает ненадёжный ввод.
Однако в реальной реализации традиционная модель «доверять фронтенду» была перенесена на сервер, что привело к уязвимости.
Вывод: при размывании границ фронтенда и бэкенда необходимо пересматривать модели безопасности
Уязвимость React/Next.js демонстрирует, что переход от чисто клиентской библиотеки к серверно‑клиентской среде требует пересмотра модели доверия. В классических архитектурах (React + DRF/FastAPI) такой уязвимости нет, потому что React не исполняется на сервере.
В Next.js, где протоколы сериализации/десериализации React глубоко интегрированы в серверный код, необходимо одновременно улучшать функциональность и создавать новые механизмы проверки и защиты.
В будущем появятся ещё более «гибридные» фреймворки, и этот случай служит предупреждением: удобство не должно заменять надёжную модель безопасности.
Комментариев нет.