NGINX 1.31.5 маршрутизирует по телу JSON, а njs 1.0.1 закрыл три CVE
Открытый NGINX научился выбирать location по содержимому JSON-запроса и получил Control API из NGINX Plus, а njs 1.0.1 исправил обход проверки доступа и два падения worker-процесса.

2 сентября вышел NGINX 1.31.5 с четырьмя новыми возможностями в открытом ядре: блок location теперь выбирается по любой переменной, а не только по пути URI, тело запроса можно прочитать до выбора location, появился встроенный модуль разбора JSON, и в открытую версию перенесён Control API из коммерческого NGINX Plus. Об этом сообщили в блоге NGINX Дилан Шварц и Алессандро Фаэль Гарсия. В тот же день вышел njs 1.0.1, модуль JavaScript для NGINX, который закрывает три уязвимости, одна из которых позволяла обойти проверку доступа в директиве js_access.
Для тех, кто держит NGINX перед API, это меняет привычную схему. Раньше, чтобы направить запрос к /graphql или /mcp на разные бэкенды в зависимости от того, что лежит внутри JSON, приходилось подключать njs или Lua. Теперь то же самое делается директивами конфигурации на C без скриптового рантайма. Тем, кто уже использует njs с js_access, обновление стоит поставить в первую очередь: до 1.0.1 ошибка внутри асинхронной проверки приводила к тому, что NGINX продолжал обрабатывать запрос, как будто проверка пройдена.
Ключевые выводы
- NGINX 1.31.5 от 2 сентября 2026 года: location по переменной (predicate locations), директива
client_body_early_read, модульngx_http_json_module, Control API из NGINX Plus R37.0. - Control API не имеет аутентификации: проект рекомендует запускать его только на UNIX-сокете с правами для привилегированных пользователей и не выставлять на сетевой порт.
- Главное исправление 1.31.5: use-after-free при буферизованном проксировании к клиенту по HTTP/2, из-за которого клиенту могла уйти освобождённая память или падал worker.
- njs 1.0.1 закрыл CVE-2026-18329 (обход
js_access, появился в 0.9.9), CVE-2026-78222 (падение worker при пустой reason phrase у upstream, с 0.5.1) и CVE-2026-78689 (переполнение буфера вxml.exclusiveC14n(), с 0.7.10). - Исходники и бинарные пакеты для основных дистрибутивов Linux уже доступны на nginx.org.
Что изменилось в маршрутизации
Авторы блога объясняют новую схему через почту. URI вроде /api/v1/checkout это адрес на конверте, заголовки Authorization и User-Agent это штампы, а тело запроса с JSON это само письмо внутри. Классический NGINX сортировал письма по адресу на конверте: выбирал location по URI, а тело обычно читалось уже после выбора location. Проблема в том, что современные клиенты, от GraphQL и JSON-RPC до MCP-серверов для ИИ-агентов и краулеров, шлют совершенно разные операции на один и тот же адрес /api/v1, /graphql или /mcp.
В 1.31.5 конверт можно вскрыть до сортировки. Четыре новые возможности складываются в одну цепочку: прочитать тело раньше, вытащить из JSON нужное поле в переменную, использовать переменную как условие выбора location.
Predicate locations: location по любой переменной
Любая переменная становится предикатом, если написать её в определении location (nginx/nginx#1633). Блок срабатывает, когда переменная непустая и не равна нулю. Условие может учитывать что угодно: подсеть клиента, клиентский сертификат, заголовки, результат map или поле из тела запроса. Пример из блога:
По словам разработчиков, условия вычисляются на уровне C без скриптового рантайма. До сих пор сложную логику выбора location собирали из map, if и внутренних редиректов либо выносили в njs и Lua.
client_body_early_read: тело до выбора location
По умолчанию NGINX читает заголовки, выбирает location и только затем буферизует тело. Директива client_body_early_read меняет порядок: тело буферизуется до сопоставления location, а разбирает его уже отдельный JSON-модуль (nginx/nginx#1641). Проект называет два сценария: маршрутизация по содержимому, когда вызов инструмента tools/call в MCP уходит на свой сервис вместо общего /mcp, и проверка на границе, когда слишком большой или некорректный payload отклоняется, ограничивается по частоте или валидируется до того, как уйдёт на бэкенд. Раньше для учёта отдельных вызовов инструментов агентов NGINX предлагал модуль MCP Observability на njs.
ngx_http_json_module: поля JSON в переменные
Новый модуль разбирает буферизованный JSON и кладёт поля, включая вложенные, в обычные переменные NGINX (nginx/nginx#1642). Дальше переменную можно подать в map или прямо в предикат location. Пример вытаскивает поле method из тела POST-запроса. В анонсе аргументы приведены в обратном порядке; по исходному коду модуля директива принимает сначала имя новой переменной, затем источник и путь:
В паре с client_body_early_read переменные из JSON заполняются до выбора location. Полное описание директив модуля разработчики обещают в отдельных статьях: в блоге анонсированы три разбора, про predicate locations, про Control API и про маршрутизацию по телу запроса.
Control API пришёл из NGINX Plus, но без аутентификации
Control API впервые появился в NGINX Plus R37.0 LTS, а теперь перенесён в открытую версию (nginx/nginx#1626). Он даёт программный доступ к состоянию процессов и конфигурации по адресам /1/control/processes и /1/control/config и позволяет перезагружать конфигурацию с синхронным ответом в JSON. Раньше перезагрузка делалась через nginx -s reload или сигнал, а результат приходилось ловить в /var/log/nginx/error.log: опечатка в конфигурации или проблема с правами на сокет обнаруживались уже постфактум. Теперь ошибка возвращается в HTTP-ответе, что удобно для конвейеров деплоя и инструментов infrastructure as code.
Для безопасного использования проект советует запускать NGINX с привязкой API к UNIX-сокету:
Control API работает и на сетевом порту, но команда NGINX прямо не рекомендует так делать: «API предоставляет открытый неаутентифицированный интерфейс к внутренностям NGINX и должен быть привязан к защищённой файловой системе, доступной только привилегированным пользователям» (перевод редакции). Путь к сокету в примере, /tmp/nginx.sock, взят из блога; на боевом сервере его стоит вынести в каталог с ограниченными правами.Какие ошибки исправили и почему авторы советуют обновиться
Главной причиной обновиться разработчики называют исправление в буферизованном проксировании (nginx/nginx#1664). При ошибке на стороне клиента функция ngx_event_pipe_drain_chains() возвращала в пул все буферы, включая цепочку p->busy, которую ещё использовали фильтры вывода. По HTTP/1.1 это оставалось незаметным, потому что запрос завершался сразу. По HTTP/2 флаг ошибки ставился на фиктивное соединение, обработчик записи продолжал отправлять DATA-фреймы, указывающие на освобождённую память, и клиент мог получить содержимое кучи, а worker упасть на незамапленной странице. По словам авторов, вредоносный ввод для этого не требовался: в упрощённом описании анонса хватало ошибки в фильтре тела ответа и медленного клиента, у которого накапливались фреймы; в pull request сценарий воспроизведения описан подробнее, с настройками временных файлов и особым ответом upstream. Затронута любая конфигурация с proxy_buffering on и клиентами по HTTP/2.
- Worker без свободных файловых дескрипторов теперь завершается штатно. Раньше при заполненной таблице дескрипторов вызов
recvmsg()сSCM_RIGHTSне мог прочитать канал управления, worker закрывал свой конец канала и больше не получал сигнал завершения, то есть висел после reload (nginx/nginx#1662). - Таймер повторного включения accept удаляется вместе с listen-соединением: иначе после
NGX_CMD_QUITв логах появлялосьaccept4() failed (9: Bad file descriptor). - Имена параметров
fastcgi_paramот 128 байт иuwsgi_paramот 256 байт теперь кодируются с правильной длиной. Раньше поле длины обрезалось до одного байта, запрос к бэкенду получался повреждённым, PHP-CGI сбрасывал соединение, и NGINX отвечал 502 на каждый запрос к такому location (nginx/nginx#1648). SCGI не затронут. - Три проверки входных данных: длины ответов memcached вблизи
NGX_MAX_OFF_T_VALUEотклоняются с 502, начало диапазонаRangeу самого максимума в модуле slice игнорируется с ответом 416, а CRYPTO-фреймы QUIC в 1-RTT-пакетах после завершения рукопожатия отвергаются с ошибкойunexpected_message. Первые две ошибки были неопределённым поведением и роняли сборки с-fsanitize=signed-integer-overflow. - Исправлен расчёт переполнения длины при формировании JSON в
ngx_json_obj_length().
Что закрыл njs 1.0.1
njs, модуль, который добавляет в NGINX JavaScript, получил версию 1.0.1 в тот же день. В списке изменений три записи с пометкой Security.
- CVE-2026-18329: обход контроля доступа в
js_access. Если асинхронное продолжение чтения тела запроса бросало исключение или завершалось необработанным rejection, NGINX продолжал обрабатывать запрос так, будто проверкаjs_accessпрошла. Ошибка появилась в njs 0.9.9; нашёл её Та Дык Тхиен. - CVE-2026-78222: падение worker-процесса при чтении
Response.statusText, когда upstream вернул строку статуса с пустой reason phrase. Ошибка присутствовала с версии 0.5.1. - CVE-2026-78689: переполнение буфера в куче при разборе списка префиксов пространств имён, переданного в
xml.exclusiveC14n(). Ошибка с версии 0.7.10; о ней сообщили исследователи из Cyera и evilgensec.
Помимо уязвимостей, в 1.0.1 исправлены use-after-free, аварийные завершения worker и утечки при циклических ссылках между объектами Fetch, HTTP-запроса и Stream-сессии в движке QuickJS, переполнение буфера на стеке при экспорте RSA-ключей длиннее 4096 бит в JWK через crypto.subtle.exportKey(), шифрование и расшифровка RSA-OAEP с SHA-256 и SHA-384, проверка значений заголовков Fetch и имён в r.headersOut, а также целей редиректа в r.return(). Добавлены глобальные функции btoa() и atob() в движке QuickJS и совместимость с quickjs-ng 0.16.0 и новее.
Что делать администратору
- Если в конфигурации есть
js_access, обновить njs до 1.0.1 сразу: до этого ошибка внутри проверки доступа открывала запрос вместо того, чтобы его отклонить. - Если NGINX проксирует с
proxy_buffering onи принимает HTTP/2, обновить ядро до 1.31.5: это исправление разработчики называют главной причиной обновления. - Ветка 1.31 это mainline, где новые возможности появляются первыми. Predicate locations,
client_body_early_read, JSON-модуль и Control API есть только здесь; в стабильной ветке их пока нет, и о сроках переноса в блоге не сказано. - Пробуя Control API, привязывать его только к UNIX-сокету в каталоге с ограниченными правами. Аутентификации у API нет.
NGINX 1.31.5 доступен в исходниках и бинарными пакетами для основных дистрибутивов Linux. Предыдущая версия 1.31.4 вышла 19 августа и добавила PROXY protocol v2 в модули stream и mail. По каждой из четырёх новых возможностей команда NGINX обещает отдельные статьи с примерами конфигурации, и редакция вернётся к теме, когда появится документация директив JSON-модуля.
Источники: NGINX 1.31.5: Control API, predicate locations, early body inspection, and more (NGINX Community Blog), CHANGES nginx 1.31.5, Changes with njs 1.0.1, Релиз nginx 1.31.5 на GitHub
Изображение на обложке: Изображение: F5 NGINX












