Продолжаем разбирать V8 — на этот раз по слоям.
Если первая часть была больше про историю, то теперь спускаемся вглубь: как именно движок исполняет JavaScript, какие компиляторы внутри него живут и зачем их так много.
Ignition, TurboFan, Sparkplug, Maglev — всё это может звучать пугающе, но на самом деле это слоистая архитектура V8, которую я постаралась разложить по полочкам.
Погружение в V8. Часть 2. Из чего состоит движок.
Если первая часть была больше про историю, то теперь спускаемся вглубь: как именно движок исполняет JavaScript, какие компиляторы внутри него живут и зачем их так много.
Ignition, TurboFan, Sparkplug, Maglev — всё это может звучать пугающе, но на самом деле это слоистая архитектура V8, которую я постаралась разложить по полочкам.
Погружение в V8. Часть 2. Из чего состоит движок.
1❤17👍3
Вышла третья часть моего цикла про V8!
На этот раз про то, как V8 разбирает код и что происходит ещё до первого его выполнения. Посмотрим на AST, preparser, сканер, скоупы и байткод.
Погружение в v8. Часть 3. Парсинг, AST и анализ кода.
На этот раз про то, как V8 разбирает код и что происходит ещё до первого его выполнения. Посмотрим на AST, preparser, сканер, скоупы и байткод.
Погружение в v8. Часть 3. Парсинг, AST и анализ кода.
4❤15👍2
Я уже немного писала про Garbage Collection ранее в канале, но цикл про V8 будет неполный без отдельной статьи на эту тему, так что вот:
Погружение в v8. Часть 4. Управление памятью и сборка мусора.
В новой части разбираемся, как движок управляет памятью, оптимизирует сборку мусора и не только.
Погружение в v8. Часть 4. Управление памятью и сборка мусора.
В новой части разбираемся, как движок управляет памятью, оптимизирует сборку мусора и не только.
🔥21❤1
Новая часть цикла про V8 ждёт вас.
И в ней не только изучим теорию некоторых интересных оптимизаций, но и посмотрим на прикладные советы для нашего повседневного JS, которые помогут движку делать свою работу ещё лучше! 🙊
Погружение в v8. Часть 5. Скрытые оптимизации.
И в ней не только изучим теорию некоторых интересных оптимизаций, но и посмотрим на прикладные советы для нашего повседневного JS, которые помогут движку делать свою работу ещё лучше! 🙊
Погружение в v8. Часть 5. Скрытые оптимизации.
🔥16
Финал цикла про V8!
Здесь рассмотрим, как движок взаимодействует с браузером, Node.js и WebAssembly, а также поговорим про будущее.
Погружение в v8. Часть 6. От среды к среде.
Этот цикл был для меня небольшим путешествием внутрь движка.
Надеюсь, читать его было так же интересно, как мне — писать ❤️
Здесь рассмотрим, как движок взаимодействует с браузером, Node.js и WebAssembly, а также поговорим про будущее.
Погружение в v8. Часть 6. От среды к среде.
Этот цикл был для меня небольшим путешествием внутрь движка.
Надеюсь, читать его было так же интересно, как мне — писать ❤️
1❤14🔥5
Интересная тема — как вообще организовывается авторизация в разных проектах.
У кого-то это JWT-токены (тут я когда-то писала про них), у кого-то — сессионные куки, у кого-то и то, и другое.
JWT удобен тем, что сервер выписывает токен, подписывает своим ключом, а клиент просто прикладывает его к каждому запросу. Но если токен утёк, то всё равно до конца его срока жизни он будет валиден — нет никакого механизма отзыва. Поэтому такие токены делают короткоживущими и добавляют второй — refresh-токен, чтобы обновлять сессию.
Дальше начинается самое интересное — где хранить этот refresh-токен. В localStorage небезопасно, в куках — только если httpOnly. А в чистом SPA это уже сложнее, потому что нельзя управлять такими куками с фронта.
Тут кто-то может начать задумываться про Backend-for-Frontend (BFF). Но, как по мне, делать BFF только ради авторизации — странное решение. Это может быть одним из пунктов, но не единственным. Если у вас есть ещё причины (например, хочется проксировать запросы, фильтровать данные или использовать SSR) — тогда да, BFF может дать больше гибкости и безопасности.
В целом, у SPA возможностей в плане авторизации гораздо меньше, чем у фронта с BFF. Поэтому если вы никак не можете менять логику авторизации на бэкенде — BFF может стать неплохим компромиссом.
И ещё важно помнить про блокировку пользователей и отзыв токенов. Даже если JWT живёт неделю, должна быть возможность ограничить доступ человеку, которого заблокировали. Иногда это решается централизованным чёрным списком токенов/сессий, иногда — проверкой статуса при каждом запросе.
Авторизация вообще тесно связана с безопасностью.
Бессмысленно ставить JWT и при этом не защититься от XSS, CSRF и не настроить CSP-политику. Это как поставить стальные ворота, но оставить рядом огромную дыру в стене — мощно, но бесполезно.
Ну и отдельный мир — это SSO (OAuth, OpenID, и вот это всё). Я с ним пока не работала, но тема явно интересная и заслуживает отдельного разбора.
А с чем сталкивались вы? Какие подходы к авторизации вам кажутся удачными, а какие — категорически нет?
У кого-то это JWT-токены (тут я когда-то писала про них), у кого-то — сессионные куки, у кого-то и то, и другое.
JWT удобен тем, что сервер выписывает токен, подписывает своим ключом, а клиент просто прикладывает его к каждому запросу. Но если токен утёк, то всё равно до конца его срока жизни он будет валиден — нет никакого механизма отзыва. Поэтому такие токены делают короткоживущими и добавляют второй — refresh-токен, чтобы обновлять сессию.
Дальше начинается самое интересное — где хранить этот refresh-токен. В localStorage небезопасно, в куках — только если httpOnly. А в чистом SPA это уже сложнее, потому что нельзя управлять такими куками с фронта.
Тут кто-то может начать задумываться про Backend-for-Frontend (BFF). Но, как по мне, делать BFF только ради авторизации — странное решение. Это может быть одним из пунктов, но не единственным. Если у вас есть ещё причины (например, хочется проксировать запросы, фильтровать данные или использовать SSR) — тогда да, BFF может дать больше гибкости и безопасности.
В целом, у SPA возможностей в плане авторизации гораздо меньше, чем у фронта с BFF. Поэтому если вы никак не можете менять логику авторизации на бэкенде — BFF может стать неплохим компромиссом.
И ещё важно помнить про блокировку пользователей и отзыв токенов. Даже если JWT живёт неделю, должна быть возможность ограничить доступ человеку, которого заблокировали. Иногда это решается централизованным чёрным списком токенов/сессий, иногда — проверкой статуса при каждом запросе.
Авторизация вообще тесно связана с безопасностью.
Бессмысленно ставить JWT и при этом не защититься от XSS, CSRF и не настроить CSP-политику. Это как поставить стальные ворота, но оставить рядом огромную дыру в стене — мощно, но бесполезно.
Ну и отдельный мир — это SSO (OAuth, OpenID, и вот это всё). Я с ним пока не работала, но тема явно интересная и заслуживает отдельного разбора.
А с чем сталкивались вы? Какие подходы к авторизации вам кажутся удачными, а какие — категорически нет?
🤔6❤5
Кстати, уже завтра я буду выступать на FrontendConf с докладом про нативные модули и node-gyp. Покажу разные интересные графики, и пообсуждаем целесообразность использования такого подхода в разработке на Node.js 💫
frontendconf.ru
Анастасия Котова на FrontendConf 2025
Node.js умеет многое, но не все. Когда нужно выжать максимум из вычислений или достучаться до «железа», на сцену выходят нативные модули. Node-gyp — не самая популярная технология, но именно она позволяет быстро подключить мощь C++ к Node.js и заметно ускорить…
❤12🔥6
Node.js на стероидах.pdf
12.7 MB
Вчера выступила с докладом про нативные модули (или native addons) в Node.js. Это расширения, написанные на C/C++, которые обеспечивают доступ к высокопроизводительным или низкоуровневым функциям, недоступным на чистом JavaScript.
По традиции, делюсь ссылкой на полезные материалы и приклыдываю свои слайды:
👉 Материалы на github
(в презентации много графиков с бенчмарками, а в материалах исходный код для них)
Если у вас есть вопросы по этой теме — пишите мне в личку @startpoint_forl, в сообщения этого канала или в комментарии под этим постом.
Также можете просто поставить реакцию, мне будет очень приятно)
По традиции, делюсь ссылкой на полезные материалы и приклыдываю свои слайды:
👉 Материалы на github
(в презентации много графиков с бенчмарками, а в материалах исходный код для них)
Если у вас есть вопросы по этой теме — пишите мне в личку @startpoint_forl, в сообщения этого канала или в комментарии под этим постом.
Также можете просто поставить реакцию, мне будет очень приятно)
❤27🔥13
А теперь — пост про то, что не влезло в доклад о нативных модулях: связь Node.js и C++, обработку исключений внутри модулей, команды для компиляции и работу с памятью.
Ещё немного про нативные модули в Node.js
Ну а я уже думаю над темами следующих интересных статей и докладов для вас. Будем копать глубже!)
Ещё немного про нативные модули в Node.js
Ну а я уже думаю над темами следующих интересных статей и докладов для вас. Будем копать глубже!)
Telegraph
Ещё немного про нативные модули в Node.js
Введение На прошлой неделе я выступала с докладом о нативных модулях Node.js — как они устроены, зачем нужны и в каких случаях стоит использовать C++ рядом с JavaScript, а в каких нет. Материалы доклада можно посмотреть в этом посте. А здесь мне хочется чуть…
🔥6❤2
В одной из частей цикла про V8 я рассказывала, почему важно сохранять массивы однородными и без «дыр» — тогда движок может их эффективнее оптимизировать.
В блоге V8 есть отличная статья, где показано, как можно посмотреть, какой тип элементов сейчас у массива и от чего он меняется. Ниже — краткий гайд, как повторить это у себя локально.
Нам понадобится отладочная (debug) сборка V8 — с ней можно смотреть не только на типы элементов, но и, например, как движок оптимизирует или деоптимизирует код.
1. Сначала ставим depot_tools, чтобы получить утилиту
2. Потом по инструкции из документации подтягиваем исходники V8.
На macOS важно: если у вас установлены только XCode Command Line Tools, их нужно удалить и поставить полноценный XCode. Подробности — здесь.
3. Дальше собираем движок:
У меня сборка шла очень долго, поэтому лучше сразу собирать правильную версию (release или debug). Debug-версия обладает бОльшими возможностями для логирования разной информации.
После сборки можно запустить движок в REPL-режиме:
Флаг
Пример вывода:
Можно и просто передать файл с кодом:
А если добавить флаг
Результат:
Дальше можно экспериментировать: добавлять в массив
В блоге V8 есть отличная статья, где показано, как можно посмотреть, какой тип элементов сейчас у массива и от чего он меняется. Ниже — краткий гайд, как повторить это у себя локально.
Нам понадобится отладочная (debug) сборка V8 — с ней можно смотреть не только на типы элементов, но и, например, как движок оптимизирует или деоптимизирует код.
1. Сначала ставим depot_tools, чтобы получить утилиту
gclient.2. Потом по инструкции из документации подтягиваем исходники V8.
На macOS важно: если у вас установлены только XCode Command Line Tools, их нужно удалить и поставить полноценный XCode. Подробности — здесь.
3. Дальше собираем движок:
gclient sync
cd /path/to/v8
git pull && gclient sync
tools/dev/gm.py arm64.debug # debug-сборка для arm на macOS
У меня сборка шла очень долго, поэтому лучше сразу собирать правильную версию (release или debug). Debug-версия обладает бОльшими возможностями для логирования разной информации.
После сборки можно запустить движок в REPL-режиме:
v8/out/arm64.debug/d8 --allow-natives-syntax
Флаг
--allow-natives-syntax позволяет использовать специальные отладочные функции, такие как %DebugPrint(array):
d8> const array = [1, 2, 3]; %DebugPrint(array);
Пример вывода:
DebugPrint: 0x2dcc00389c0d: [JSArray]
- map: 0x2dcc0005b7d1 <Map[16](PACKED_SMI_ELEMENTS)> [FastProperties]
- prototype: 0x2dcc0005b7f9 <JSArray[0]>
- elements: 0x2dcc0006ca25 <FixedArray[3]> [PACKED_SMI_ELEMENTS (COW)]
- length: 3
- properties: 0x2dcc000007bd <FixedArray[0]>
.....
Можно и просто передать файл с кодом:
v8/out/arm64.debug/d8 --allow-natives-syntax ~/Documents/all-examples/v8/test.js
А если добавить флаг
--trace-elements-transitions, то движок будет печатать все изменения типа:
// test2.js
const array = [1, 2, 3];
array[3] = 4.56;
v8/out/arm64.debug/d8 --trace-elements-transitions ~/Documents/all-examples/v8/test2.js
Результат:
elements transition [PACKED_SMI_ELEMENTS -> PACKED_DOUBLE_ELEMENTS] in ~+15 at test2.js:1 for 0x2cf600389c45 <JSArray[3]> from 0x2cf60006ca0d <FixedArray[3]> to 0x2cf600389c55 <FixedDoubleArray[22]>
Дальше можно экспериментировать: добавлять в массив
undefined, -0, пропущенные индексы — и смотреть, как V8 сразу меняет внутренний тип. Это наглядный способ понять, как движок анализирует наш код и почему иногда одно лишнее значение или неправильная инициализация может замедлить выполнение.👍11🔥6
Как можно профилировать память в Node.js?
Один из базовых способов — heap snapshot через Chrome DevTools. Для этого нужно запустить Node с флагом --inspect:
После запуска можно открыть в браузере
Если же мы хотим отслеживать метрики памяти в реальном времени, то можно сделать это с помощью PerformanceObserver. Он слушает события, создаваемые системой perf_hooks. Например, можно смотреть, как растёт heap и когда происходят сборки мусора:
Если нужно больше деталей о работе V8 и нет времени писать лишний код, можно просто запустить процесс с флагом --trace_gc:
В консоли появятся строки вроде:
Здесь видно тип сборки мусора, её длительность и изменение размера кучи.
Ну а самый простой и быстрый способ получить обзор текущего состояния, и, скорей всего, вы про него уже не раз слышали — метод process.memoryUsage. Он возвращает данные по основным сегментам памяти:
Один из базовых способов — heap snapshot через Chrome DevTools. Для этого нужно запустить Node с флагом --inspect:
node --inspect app.js.После запуска можно открыть в браузере
chrome://inspect и подключиться к процессу в DevTools и на вкладке Memory снять снимок кучи. Он показывает, какие объекты занимают память.Если же мы хотим отслеживать метрики памяти в реальном времени, то можно сделать это с помощью PerformanceObserver. Он слушает события, создаваемые системой perf_hooks. Например, можно смотреть, как растёт heap и когда происходят сборки мусора:
import { PerformanceObserver, performance } from 'node:perf_hooks';
const obs = new PerformanceObserver((items) => {
for (const entry of items.getEntries()) {
console.log(`[${entry.entryType}]`, entry.name, entry.duration.toFixed(2), 'ms');
}
});
obs.observe({ entryTypes: ['gc'] });
Если нужно больше деталей о работе V8 и нет времени писать лишний код, можно просто запустить процесс с флагом --trace_gc:
node --trace_gc app.js.В консоли появятся строки вроде:
[89452:0xa81400000] 3103 ms: Scavenge 18.9 (27.7) -> 18.7 (50.7) MB, pooled: 0 MB, 4.29 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
Здесь видно тип сборки мусора, её длительность и изменение размера кучи.
Ну а самый простой и быстрый способ получить обзор текущего состояния, и, скорей всего, вы про него уже не раз слышали — метод process.memoryUsage. Он возвращает данные по основным сегментам памяти:
const mem = process.memoryUsage();
console.log(`Heap used: ${(mem.heapUsed / 1024 / 1024).toFixed(2)} MB`);
console.log(`RSS: ${(mem.rss / 1024 / 1024).toFixed(2)} MB\n`);
👍12❤7👌2🤝1
Настя Котова // Frontend & Node.js
Node.js на стероидах.pdf
А вот и видео с моего выступления на FrontendConf. Так что если хотели посмотреть/послушать — велком!)
https://youtu.be/ZaoRJs5HD8k
https://youtu.be/ZaoRJs5HD8k
YouTube
Node.js на стероидах: как и зачем писать нативные модули / Анастасия Котова
Крупнейшая профессиональная конференция фронтенд-разработчиков в России
FrontendConf 2025
Презентация и тезисы:
https://frontendconf.ru/moscow/2025/abstracts/15502
Node.js умеет многое, но не все. Когда нужно выжать максимум из вычислений или достучаться…
FrontendConf 2025
Презентация и тезисы:
https://frontendconf.ru/moscow/2025/abstracts/15502
Node.js умеет многое, но не все. Когда нужно выжать максимум из вычислений или достучаться…
🔥17❤7👍1👏1
Как много мы здесь говорили про что-то фундаментальное последнее время — про нативные модули в Node.js, про libuv, про V8… Ещё и исходный код многих этих вещей написан не на JavaScript.
Мне захотелось почитать чего-то родного, немножечко моих любимых js-исходников. И так родилась следующая тема.
Как работают сборщики: Webpack
Сегодня начнём говорить про сборщики и про то, как они работают. Пока в планах две части — первая про Webpack, вторая про Vite. Но, возможно, какие-то частные моменты я захочу раскрыть и покопать сильнее.
Также, если вам интересно что-то конкретное — мои комментарии и сообщения в этом канале всегда открыты)
Мне захотелось почитать чего-то родного, немножечко моих любимых js-исходников. И так родилась следующая тема.
Как работают сборщики: Webpack
Сегодня начнём говорить про сборщики и про то, как они работают. Пока в планах две части — первая про Webpack, вторая про Vite. Но, возможно, какие-то частные моменты я захочу раскрыть и покопать сильнее.
Также, если вам интересно что-то конкретное — мои комментарии и сообщения в этом канале всегда открыты)
1🔥37❤1😁1
В продолжение разговора про Webpack стоит немного поговорить про code splitting.
На заре сборщиков мы жили в достаточно простом мире: был один entry point — был один большой выходной файл. Это работало нормально, пока проекты не начали разрастаться. Оказалось, что огромный бандл — это отсутствие нормального кеширования, необходимость перекачивать весь код заново при любых изменениях и замедления загрузки.
Из этой боли родилась идея code splitting. Какое-то время для этого использовали только lazy-импорты, и разработчики сами должны были подсказывать сборщику, где стоит разрезать проект. Но со временем стало понятно, что это ограничивает архитектуру. Поэтому последние версии Webpack (начиная с 4-й) пошли другим путём: они научились анализировать граф модулей глубже и стали достаточно “умными”, чтобы делить код даже без lazy-импортов.
Магия спрятана в настройках оптимизации. Например, можно включить splitChunks для всех чанков и указать желаемый максимальный размер:
Теперь Webpack смотрит на итоговый entry-чанк, понимает, что он слишком большой, и начинает аккуратно вырезать из него фрагменты модулей, формируя дополнительные чанки. Импорт остаётся обычным, при этом код распределяется по нескольким файлам, которые браузер загрузит параллельно и будет кешировать независимо.
Результат на моём маленьком демо:
Все файлы будут загружены сразу, и это ожидаемое поведение: вебпак просто оптимизирует структуру бандла, но не делает его динамическим. Для ленивой загрузки по-прежнему нужны
При этом полностью отказываться от объединения модулей (.ts, .css, .tsx и т.д.) в чанки не стоит даже при работе с HTTP/2 — это показывают исследования и результаты бенчмарков (пример). Поэтому code splitting в Webpack является хорошим компромиссным решением, которое повышает скорость загрузки.
UPD: В комментариях также есть интересные рассуждения на тему причин появления code splitting и его эффективности.
На заре сборщиков мы жили в достаточно простом мире: был один entry point — был один большой выходной файл. Это работало нормально, пока проекты не начали разрастаться. Оказалось, что огромный бандл — это отсутствие нормального кеширования, необходимость перекачивать весь код заново при любых изменениях и замедления загрузки.
Из этой боли родилась идея code splitting. Какое-то время для этого использовали только lazy-импорты, и разработчики сами должны были подсказывать сборщику, где стоит разрезать проект. Но со временем стало понятно, что это ограничивает архитектуру. Поэтому последние версии Webpack (начиная с 4-й) пошли другим путём: они научились анализировать граф модулей глубже и стали достаточно “умными”, чтобы делить код даже без lazy-импортов.
Магия спрятана в настройках оптимизации. Например, можно включить splitChunks для всех чанков и указать желаемый максимальный размер:
module.exports = {
entry: './src/index.js',
optimization: {
splitChunks: {
chunks: 'all',
maxSize: 8000
}
},
...
}
Теперь Webpack смотрит на итоговый entry-чанк, понимает, что он слишком большой, и начинает аккуратно вырезать из него фрагменты модулей, формируя дополнительные чанки. Импорт остаётся обычным, при этом код распределяется по нескольким файлам, которые браузер загрузит параллельно и будет кешировать независимо.
Результат на моём маленьком демо:
Было: main.js (24KB)
Стало:
├── main.js (3.3KB)
├── 648.js (3.1KB)
├── 14.js (2.6KB)
├── 339.js (2.2KB)
└── 812.js (1.4KB)
Все файлы будут загружены сразу, и это ожидаемое поведение: вебпак просто оптимизирует структуру бандла, но не делает его динамическим. Для ленивой загрузки по-прежнему нужны
import(). Но в ситуациях, когда проект сложно разделить руками, а хочется хотя бы улучшить кеш и скорость начальной загрузки за счёт нескольких параллельных файлов, такая оптимизация работает хорошо — особенно в сочетании с переходом на HTTP/2.При этом полностью отказываться от объединения модулей (.ts, .css, .tsx и т.д.) в чанки не стоит даже при работе с HTTP/2 — это показывают исследования и результаты бенчмарков (пример). Поэтому code splitting в Webpack является хорошим компромиссным решением, которое повышает скорость загрузки.
UPD: В комментариях также есть интересные рассуждения на тему причин появления code splitting и его эффективности.
❤8🔥2👍1
Сегодня день начался не с кофе, а с внепланового обновления React и Next.js 💃
Если вы пропустили, вчера была исправлена критическая уязвимость в Rect 19 и Next.js 15, связанная с серверными компонентами. Также затронуло ещё несколько пакетов.
Для исправления достаточно обновить patch-версии затронутых библиотек на своём проекте.
Подробнее можно почитать в блоге React — https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components
Если вы пропустили, вчера была исправлена критическая уязвимость в Rect 19 и Next.js 15, связанная с серверными компонентами. Также затронуло ещё несколько пакетов.
Для исправления достаточно обновить patch-версии затронутых библиотек на своём проекте.
Подробнее можно почитать в блоге React — https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components
Please open Telegram to view this post
VIEW IN TELEGRAM
react.dev
Critical Security Vulnerability in React Server Components – React
The library for web and native user interfaces
❤2👍1🔥1
Наконец закончила вторую часть про Vite! Больше всего конечно посвятила разбору его работы в dev-режиме, а так же Hot Module Replacement.
Как работают сборщики: Vite
Как работают сборщики: Vite
🔥18👍8😁1
Уязвимости React — причины и выводы
За последние пару недель вокруг React Server Components и server actions вскрылся целый пласт уязвимостей (вот и вот). В чём же была проблема?
Server actions выглядят в коде как обычные коллбеки: мы пишем функцию с
В первой версии сервер доверял данным из запроса слишком сильно. Имя функции, которую нужно вызвать, приходило от клиента и использовалось напрямую для поиска экспорта в модуле. Проверки, что это действительно ожидаемый server action, что это собственное свойство модуля, а не что-то из прототипа, были недостаточно строгими. В итоге получалось, что сервер брал недоверенные данные и использовал их для выбора и выполнения кода. Это и привело к возможности удалённого выполнения кода без авторизации.
После того как эту дыру закрыли, стало понятно, что проблема глубже, чем просто имя модуля. Даже если код больше нельзя выполнить, сервер всё равно принимает сложный payload, парсит его, резолвит модули, оборачивает всё в промисы и пытается корректно обработать ошибочные сценарии. И этим тоже можно было воспользоваться: появились варианты запросов, которые не приводили к выполнению кода, но заставляли сервер долго и дорого думать, зацикливаться на обработке промисов и фактически ложиться по CPU. Так появилась DoS-уязвимость: если нельзя выполнить код, можно попытаться сломать сервер через его внутреннюю логику.
Третья проблема выросла из того же корня. В процессе обработки некорректных server action payload’ов сервер иногда формировал ответы или ошибки, в которых оказывалось слишком много внутренней информации. В отдельных сценариях это позволяло получить исходный код серверных функций или детали их реализации. Код не выполнялся, сервер не падал, но граница между «внутренним» и «внешним» снова оказывалась размыта.
Если смотреть на всё это вместе, становится видно, что это не три независимые уязвимости, а одна цепочка. Серверные компоненты и экшены — это по сути RPC поверх HTTP. И как только клиент начинает передавать серверу не просто данные, а описание того, что именно нужно сделать, безопасность становится гораздо более тонкой материей.
Всё это, на мой взгляд, хороший повод ещё раз подумать о том, что в момент, когда мы как фронтенд-разработчики выходим на сервер, правила немного меняются. Фронтенд уже следит за большим количеством вещей: UI и UX, производительность интерфейса, его состояние, кроссбраузерность и т.д. Просто теперь к этому списку всё чаще добавляются сервер, безопасность, миграции, обратная совместимость и API.
Даже на простых вещах это легко почувствовать. У нас, например, на проекте с BFF однажды возникла волна 404 во время выкатки нового релиза просто потому, что мы переименовали эндпойнты и не заложили многоступенчатую миграцию между старым клиентом и новым сервером. Никто не делал ничего «неправильно», мы просто не подумали об этом сценарии, потому что крайне редко с ним сталкиваемся. И вот такие истории с уязвимостями в 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 хорошо подсвечивают одну мысль: когда фронтенд становится ближе к серверу, про серверные риски тоже нужно начинать думать чаще.
🔥16💯6❤3😱1🙏1
В следующем году я опять планирую выступать на конференциях. И в этот раз начну с DUMP в Санкт-Петербурге!
Готовлю доклад с темой Ignition, Sparkplug, Maglev, TurboFan: как компилирует V8
Он вышел по следам моего цикла статей в V8, но будет в 100 раз подробнее именно в части работы интерпретатора и компиляторов движка 💫
Презентацию и запись (если она будет) выложу сюда после конференции. Но если есть возможность — конечно же приходите лично!
Подробнее можно почитать тут
Готовлю доклад с темой Ignition, Sparkplug, Maglev, TurboFan: как компилирует V8
Он вышел по следам моего цикла статей в V8, но будет в 100 раз подробнее именно в части работы интерпретатора и компиляторов движка 💫
Презентацию и запись (если она будет) выложу сюда после конференции. Но если есть возможность — конечно же приходите лично!
Подробнее можно почитать тут
dump-spb.ru
Анастасия Котова. Ignition, Sparkplug, Maglev, TurboFan: как компилирует V8
Современный V8 — это сложный конвейер из нескольких компиляторов. Если раньше всё работало на Ignition и TurboFan, то сегодня к ним добавились Sparkplug и Maglev. Как V8 решает, когда и какой компилятор использовать? Какие подходы к оптимизации применяет…
🔥15
А ещё к концу года хочу сделать подборку моих самых лучших статей за этот год. Поэтому буду признательна, если проголосуете, что именно вам понравилось больше всего!
(в опросе представлены не все посты, а только самые “жирненькие”, но всегда можно дополнить этот список в комментариях)
(в опросе представлены не все посты, а только самые “жирненькие”, но всегда можно дополнить этот список в комментариях)
👏4
Лучшие статьи за год на ваш взгляд
Anonymous Poll
20%
Цикл про Event Loop в Node.js
3%
Цикл про libuv
13%
Цикл про V8
3%
Статья про нативные модули в Node.js (дополнение к докладу)
15%
Две статьи про сборщики (Webpack и Vite)
47%
Я котик 🐈⬛
💅7👏1