Cайт веб-разработчика, программиста Ruby on Rails ESV Corp. Екатеринбург, Москва, Санкт-Петербург, Новосибирск, Первоуральск

Дыра есть, исправлять не будем. В Node.js нашли способ взломать почти всё, но разработчики называют это «особенностью архитектуры»

Масштаб грядущих неприятностей пока трудно даже вообразить.

В экосистеме Node.js обнаружили уязвимость, связанную с базовой логикой HTTP-клиента, которая позволяет обойти прежнюю защиту от разделения запросов. Мартино Спаньоло под ником r3verii опубликовал разбор после того, как команда Node.js отказалась считать проблему нарушением своей модели угроз. Речь идёт о механизме, который открывает возможность внедрения заголовков и формирования второго запроса в рамках одного соединения.

Проблема уходит корнями в 2018 год, когда уязвимость CVE-2018-12116 позволяла внедрять управляющие символы через особенности кодировки latin1. Тогда разработчики добавили проверку пути запроса в http.request, запретив символы вне диапазона \u0021—\u00ff. Защита срабатывала только в момент создания объекта ClientRequest. После этого свойство path оставалось обычным изменяемым полем без дополнительной валидации.

Исследование показало, что если изменить clientRequest.path после создания запроса, проверка больше не выполняется. При формировании HTTP-строки метод _implicitHeader использует текущее значение path без повторной фильтрации. В результате управляющие последовательности \r\n могут попасть напрямую в TCP-поток.

В зависимости от передаваемых данных последствия различаются. В простейшем варианте злоумышленник внедряет дополнительные HTTP-заголовки и подменяет Host или Authorization. Более серьёзный сценарий позволяет закрыть заголовки и добавить тело запроса, изменив семантику исходного обращения. Самый опасный вариант приводит к полноценному разделению запроса, когда сервер получает два самостоятельных HTTP-обращения вместо одного.

Автор проверил популярные библиотеки и выявил семь проектов с незащищённым промежутком TOCTOU. Среди них node-http-proxy, http-proxy-middleware, superagent, request и @hapi/wreck. Совокупное число загрузок превышает 160 миллионов в неделю. Схема везде одинаковая — библиотека создаёт ClientRequest, затем передаёт объект во внешние обработчики до отправки заголовков. Если обработчик меняет path на основе пользовательских данных, фильтрация обходится.

Некоторые инструменты избежали проблемы благодаря иной архитектуре. axios использует follow-redirects и не предоставляет доступ к внутреннему ClientRequest, got отправляет заголовки до передачи объекта наружу, а undici и встроенный fetch используют собственную реализацию HTTP-стека без изменяемого path.

Команда Node.js заявила через программу HackerOne, что поведение соответствует текущей логике работы и не считается уязвимостью. По их позиции, ответственность лежит на библиотеках, которые позволяют менять path после создания запроса. Автор исследования не согласился с оценкой и указал на масштаб экосистемы и наличие рабочих примеров эксплуатации.

В качестве минимального исправления предлагается повторно проверять путь непосредственно перед формированием строки запроса либо сделать свойство path защищённым через сеттер с валидацией. Пока изменений в ядре не планируется, разработчикам рекомендуется искать в коде присваивания clientRequest.path и проверять, не поступают ли значения из пользовательского ввода.

SecurityLab