Проект Node.js выпустил обновления безопасности 11 ноября 2026 года: версии 26.13.1, 24.22.3 и 22.23.5. Они закрывают 19 уязвимостей, максимальная серьёзность в каждой ветке - HIGH. Несколько ошибок позволяют неаутентифицированному клиенту уронить процесс, прочитать чужую память или пройти клиентскую аутентификацию под чужим сертификатом. Обновляться нужно тем, кто держит HTTP/2-серверы, mTLS и исходящие соединения через прокси.
Условия атаки и характер ошибок
Основная масса исправлений приходится на HTTP/2 и TLS. Это логично: именно там живут долгоживущие объекты состояния - сессии, потоки, буферы сокета, - и любая ошибка в порядке их освобождения превращается в отказ в обслуживании или чтение чужой памяти. Атаки в большинстве случаев сетевые и не требуют учётной записи: достаточно открытого порта.
HTTP/2: удалённый клиент без аутентификации
CVE-2026-123863 (CVSS 8.2) - достижимый assert при разрушении сессии. Клиент оставляет поток, который всё ещё держит заголовочные буферы, выделенные nghttp2. Поток удалён из набора сессии, но не отсоединён, поэтому при вызове деструктора счётчик памяти ненулевой и проверка равенства нулю падает. Достаточно обычного обработчика, который отвечает до того, как клиент закончил отправку тела, - то есть типичного поведения. Это вариант CVE-2024-27983 (CVSS 8.2), который прошлый патч не покрыл: тогда отсоединяли только потоки, ещё числившиеся в сессии.
CVE-2026-123865 (CVSS 7.5) - утечка памяти и файловых дескрипторов без ограничений. Атакующий отправляет GOAWAY(NO_ERROR), затем вызывает ошибку протокола, например слишком большой заголовок кадра. nghttp2 завершает сессию внутри себя, Node.js этого не замечает, сокет остаётся в CLOSE_WAIT, а всё состояние соединения висит в памяти. Стоимость для атакующего почти нулевая, эффект накапливается. Это неполный фикс CVE-2026-48937 (CVSS 5.3), где разбирался случай, когда сервер продолжает принимать данные после GOAWAY.
CVE-2026-123874 (CVSS 7.5) - use-after-free при отдаче файлов через "respondWithFile()" или "respondWithFD()". Клиент добивается освобождения потока, пока запись на сокет ещё в очереди; при завершении записи код разыменовывает освобождённый объект и делает виртуальный вызов. Условие одно: приложение отдаёт файл этими методами, остальное - настройки по умолчанию. CVE-2026-123875 (CVSS 5.9) - NULL-разыменование в том же сценарии: достаточно нескольких отменённых загрузок файла, чтобы процесс упал.
CVE-2026-123866 (CVSS 7.5) - ещё один use-after-free, теперь в буфере ответа. Поток разрушается, пока запись прижата backpressure: пир присылает RST_STREAM или срабатывает собственный таймаут потока. Буфер освобождён, ссылка на него жива, и в сокет уходит освобождённая или чужая память.
CVE-2026-123868 (CVSS 5.9) - обход лимита "maxSessionMemory". Входящие DATA, лежащие в невычитанной очереди потока, не учитываются в счётчике сессии. Пир заполняет окно однобайтовыми кадрами: на каждый уходит около 10 байт по сети, а сервер удерживает примерно 114 байт. Один поток удерживает сотни мегабайт.
CVE-2026-123828 (CVSS 5.9) - переполнение кучи на один байт в обработке Alt-Svc. Если приложение передаёт в "Http2Session.altsvc()" значение, контролируемое клиентом, например заголовок ":authority", буфер выделяется по длине origin, а нулевой терминатор пишется за его границей. Один запрос с длинным, но синтаксически корректным значением приводит к падению процесса и даёт примитив повреждения кучи.
TLS: сертификаты, SNI и кэш сессий
CVE-2026-123864 (CVSS 8.1) - чтение памяти, принадлежащей другим соединениям, включая приватные ключи TLS и запросы других пользователей. При записи нулевого числа байтов "TLSWrap::DoWrite()" вызывает "ClearOut()" напрямую. Если такая запись инициирована из JavaScript внутри "on_body" HTTP-парсера, расшифровка следующей записи TLS происходит, пока парсер ещё в середине тела. Повторный вход в "llhttp_execute()" даёт use-after-free и чтение за границей буфера, а сервер копирует эту память в ответ. Нужен обработчик, который зовёт "res.flushHeaders()" до конца тела, затем "res.end()" с непустым чанком, и любой маршрут, возвращающий или сохраняющий тело запроса. Чистый "node:http" не затронут.
CVE-2026-123872 (CVSS 8.1) - обход клиентской аутентификации через смену servername при ренегоциации TLS 1.2. Клиент, прошедший проверку для разрешённого хоста, переносит свою аутентификацию на защищённый хост с более строгой политикой. В том же исправлении отключено возобновление сессий для mTLS без session id context и запрещена смена SNI через ренегоциацию, HRR и resumption.
CVE-2026-123877 (CVSS 6.8) - перепутанные ключи кэша в "https.Agent". "getName()" выдаёт одинаковый ключ для запроса без "ca" и для запроса с "ca: []": отсутствующее значение ничего не добавляет к ключу, а пустой массив приводится к пустой строке. Политики при этом разные - доверенное хранилище по умолчанию против явно заменённого. Возобновление сессии TLS 1.3 переносит прежнюю аутентификацию, и второй запрос отправляет конфиденциальные данные серверу, который его собственная политика отвергла бы на новом соединении.
CVE-2026-123867 (CVSS 4.2) - обход авторизации по клиентскому сертификату на уровне хоста. Если сервер задаёт "addContext(host, { ca: protectedCA })" без серверного сертификата в этом SNI-контексте, CA хоста не устанавливается и рукопожатие откатывается к CA по умолчанию. Клиент с сертификатом дефолтного CA проходит на защищённый хост, а клиент с сертификатом protectedCA получает отказ.
CVE-2026-126051 (CVSS 7.5) - use-after-destroy в отложенных колбэках TLS. Хуки "SNICallback", "OCSPRequest", "newSession", "resumeSession" и "addContext()" выполняются отложенно и не проверяют, что сокет уже уничтожен. Client hello без SNI вместе с OCSP-запросом и FIN в том же тике: сокет умирает раньше колбэка, и процесс получает неперехваченное исключение.
Прокси, модель разрешений и zlib
CVE-2026-48615 (CVSS 5.9) и CVE-2026-123871 (CVSS 6.2) - учётные данные прокси в текстах ошибок "ERR_PROXY_TUNNEL" и "ERR_PROXY_INVALID_CONFIG". Первый патч вычистил креды из "ProxyConfig.href", но пропустил ещё четыре пути в той же подсистеме. Проблема в стоке: секрет попадает в stderr, логи контейнеров, агрегаторы логов и сервисы сбора падений.
CVE-2026-106071 (CVSS 3.6) - обход модели разрешений в файловой системе. Если запрошенный путь короче префикса wildcard-гранта, поиск в radix tree прекращает сравнение раньше и считает путь совпавшим: "--allow-fs-read=/etc/passwd.*" разрешает чтение "/etc/passwd". Нужно, чтобы грант лежал прямо под корнем дерева разрешений. Затронута только ветка 26.x.
CVE-2026-123829 (CVSS 2.5) - утечка имён файлов при рекурсивном наблюдении. На macOS и Windows "fs.watch()" в рекурсивном режиме проверяет право чтения только на корне, а затем передаёт в JS-колбэк все имена, которые сообщает libuv, без проверки покрытия грантом. Код под "--permission" узнаёт имена вида "sub/secret.txt", хотя "readdir" по этому каталогу запрещён. Утекают имена и тайминги, не содержимое.
CVE-2026-123862 (CVSS 2.9) - неперехватываемое исключение в потоковом zlib. "zlib.params()" ставит в очередь flush и завершает работу в колбэке записи. Если поток уничтожен раньше, как делает "pipeline()" при обрыве связи, нативного хэндла уже нет, и "ERR_INTERNAL_ASSERTION" вылетает мимо события "error". Нужен вызов "deflate.params()" при стриминге пиру, что встречается редко.
Затронутые версии
Исправления вышли в Node.js 26.13.1, 24.22.3 и 22.23.5 (все LTS). Более ранние версии этих ветвей уязвимы. CVE-2026-123863 и CVE-2026-123874 появились в 26.9.0 и 24.22.0 вместе с изменением размера окна HTTP/2, поэтому затронуты 26.x и 24.x. CVE-2026-48937 затрагивает 22.x и 24.x, CVE-2026-106071 - только 26.x, CVE-2026-126051, CVE-2026-123828 и CVE-2026-123868 - 26.x и 24.x. Остальные ошибки присутствуют во всех трёх ветках.
Если обновиться нельзя
Для HTTP/2 сократите поверхность атаки: не выставляйте его в интернет без обратного прокси с жёсткими лимитами на соединения, размеры кадров и время жизни сессии, следите за ростом RSS и количеством сокетов в CLOSE_WAIT. Если приложение отдаёт файлы через "respondWithFile()" или "respondWithFD()", временно переведите выдачу на обычный поток с явным контролем времени жизни.
Для mTLS не полагайтесь на CA, заданный для хоста без серверного сертификата в том же SNI-контексте: пропишите сертификат явно и проверьте, что клиент без нужного CA получает отказ. Отключите ренегоциацию TLS 1.2, если она не нужна. В "https.Agent" не смешивайте запросы с "ca" и без него в одном агенте.
Уберите учётные данные из URL прокси и передавайте их отдельно, а логи и отчёты о падениях, содержащие тексты ошибок Node.js, считайте потенциально чувствительными. В модели разрешений не используйте wildcard-гранты прямо под корнем дерева, а на macOS и Windows не рассчитывайте, что рекурсивное наблюдение скроет имена файлов.
Ссылки
- https://nodejs.org/en/blog/vulnerability/november-2026-security-releases
- https://www.cve.org/CVERecord?id=CVE-2026-123863
- https://www.cve.org/CVERecord?id=CVE-2026-123864
- https://www.cve.org/CVERecord?id=CVE-2026-123872
- https://www.cve.org/CVERecord?id=CVE-2026-123874
- https://www.cve.org/CVERecord?id=CVE-2026-123865
- https://www.cve.org/CVERecord?id=CVE-2026-123866
- https://www.cve.org/CVERecord?id=CVE-2026-126051
- https://www.cve.org/CVERecord?id=CVE-2026-123877
- https://www.cve.org/CVERecord?id=CVE-2026-123875
- https://www.cve.org/CVERecord?id=CVE-2026-123871
- https://www.cve.org/CVERecord?id=CVE-2024-27983