1.7Kпросмотров
15 декабря 2025 г.
Score: 1.8K
Уязвимости React — причины и выводы За последние пару недель вокруг React Server Components и server actions вскрылся целый пласт уязвимостей (вот и вот). В чём же была проблема? Server actions выглядят в коде как обычные коллбеки: мы пишем функцию с use server, вызываем её из компонента — и всё. Но под капотом это POST-запрос на сервер. Клиент отправляет payload, сервер его десериализует, понимает, какой серверный модуль нужно вызвать, и выполняет этот код уже на своей стороне. И вот именно в этом месте была самая первая, самая критичная уязвимость. В первой версии сервер доверял данным из запроса слишком сильно. Имя функции, которую нужно вызвать, приходило от клиента и использовалось напрямую для поиска экспорта в модуле. Проверки, что это действительно ожидаемый server action, что это собственное свойство модуля, а не что-то из прототипа, были недостаточно строгими. В итоге получалось, что сервер брал недоверенные данные и использовал их для выбора и выполнения кода. Это и привело к возможности удалённого выполнения кода без авторизации. После того как эту дыру закрыли, стало понятно, что проблема глубже, чем просто имя модуля. Даже если код больше нельзя выполнить, сервер всё равно принимает сложный payload, парсит его, резолвит модули, оборачивает всё в промисы и пытается корректно обработать ошибочные сценарии. И этим тоже можно было воспользоваться: появились варианты запросов, которые не приводили к выполнению кода, но заставляли сервер долго и дорого думать, зацикливаться на обработке промисов и фактически ложиться по CPU. Так появилась DoS-уязвимость: если нельзя выполнить код, можно попытаться сломать сервер через его внутреннюю логику. Третья проблема выросла из того же корня. В процессе обработки некорректных server action payload’ов сервер иногда формировал ответы или ошибки, в которых оказывалось слишком много внутренней информации. В отдельных сценариях это позволяло получить исходный код серверных функций или детали их реализации. Код не выполнялся, сервер не падал, но граница между «внутренним» и «внешним» снова оказывалась размыта. Если смотреть на всё это вместе, становится видно, что это не три независимые уязвимости, а одна цепочка. Серверные компоненты и экшены — это по сути RPC поверх HTTP. И как только клиент начинает передавать серверу не просто данные, а описание того, что именно нужно сделать, безопасность становится гораздо более тонкой материей. Всё это, на мой взгляд, хороший повод ещё раз подумать о том, что в момент, когда мы как фронтенд-разработчики выходим на сервер, правила немного меняются. Фронтенд уже следит за большим количеством вещей: UI и UX, производительность интерфейса, его состояние, кроссбраузерность и т.д. Просто теперь к этому списку всё чаще добавляются сервер, безопасность, миграции, обратная совместимость и API. Даже на простых вещах это легко почувствовать. У нас, например, на проекте с BFF однажды возникла волна 404 во время выкатки нового релиза просто потому, что мы переименовали эндпойнты и не заложили многоступенчатую миграцию между старым клиентом и новым сервером. Никто не делал ничего «неправильно», мы просто не подумали об этом сценарии, потому что крайне редко с ним сталкиваемся. И вот такие истории с уязвимостями в React хорошо подсвечивают одну мысль: когда фронтенд становится ближе к серверу, про серверные риски тоже нужно начинать думать чаще.