Вы проводите ревью и находите в коде JavaScript использование eval для парсинга JSON: var obj = eval('('+json+')'); Обсудите риски такого подхода, предложите безопасные альтернативы и укажите редкие случаи, когда eval может быть оправдан, а также меры минимизации риска.
Риски использования eval для парсинга JSON (пример: `var obj = eval('('+json+')');`) - Выполнение произвольного кода. Если строка `json` контролируется злоумышленником, она может содержать JavaScript выражения, которые будут выполнены. Пример простого трюка: если `json = "1); evil(); (1"`, то `eval('('+json+')')` выполнит `evil()`. Это основная и критическая уязвимость — удалённое выполнение кода (RCE). - XSS и компрометация окружения. В браузере это может привести к XSS, краже cookies, сессий, токенов; на сервере — к утечке данных или удалённому выполнению команд. - Обход CSP: использование eval требует разрешения `unsafe-eval` в CSP; его включение увеличивает атаку вектора. - Производительность и отладка: `eval` медленнее, сложнее профилировать и безопасно отлаживать. - Утечка лексического окружения / доступ к локальным переменным — `eval` видит внешние переменные (в некоторых режимах). - Сложность безопасной валидации: попытки «очистить» строку regex-ами часто ненадёжны и легко обходятся. Безопасные альтернативы - Стандартный: `JSON.parse(json[, reviver])`. Надёжный, быстрый, не исполняет код. Использовать всегда, когда вход — валидный JSON. - Для старых браузеров: подключить полифилл (напр., Douglas Crockford json2.js) или транспилировать/бандлить так, чтобы `JSON.parse` был доступен. - Если нужен tolerant/lenient парсинг (одиночные кавычки, комментарии, незаключённые ключи): использовать специальные парсеры, которые безопасно превращают JS-объектную нотацию в структуру данных, например: - tolerant JSON парсеры / libs: `json5`, `jju`, `hjson` — они парсят расширенные формы, но не выполняют код. - потоковые парсеры для больших потоков: `clarinet`, `oboe`. - Для сериализации/десериализации, включая функции/классы: использовать безопасные форматы и либы (например, `serialize-javascript` с осторожностью) либо явные трансформеры (replacer/reviver), а не eval. - На Node.js при необходимости ограниченного выполнения: `vm` с контекстом и таймаутом — например `vm.runInNewContext('(' + json + ')', {}, {timeout: 1000})`. Это всё ещё выполняет код, но в ограниченном контексте; не равнозначно полной безопасной песочнице, требует осторожности. Редкие случаи, когда eval может быть оправдан - Вы реально парсите не JSON, а произвольную JS-нотацию (функции, выражения, RegExp, некорректный JSON), и вход полностью доверенный (локальные конфигурационные файлы, админский интерфейс). Даже тогда лучше использовать специализированный парсер. - Инструменты разработки, REPL, отладочные/интерактивные среды, где пользователь ожидает выполнение JS и среда контролируется. - Очень специфичные legacy-сценарии, где переписать зависимый код или заменить формат невозможно и вход стопроцентно доверен. Меры минимизации риска если eval всё-таки используется - Никогда не применять eval к данным из ненадёжных источников (внешние запросы, параметры URL, тело запроса, пользовательский ввод). - Если возможна замена — заменить на `JSON.parse`. - Если нельзя избежать выполнения кода: - Выполнять в минимально привилегированном окружении: Node.js `vm.runInNewContext` с пустым контекстом и таймаутом. - Использовать реальные песочницы/реалмы (SES от Agoric, изолированные процессы, контейнеры). - Ограничить права процесса/контейнера (runtime user без прав, chroot/namespace, AppArmor/SELinux). - Валидация входа регулярными грамматиками — только как дополнительная мера; не полагаться на простые regex-решения. - Логировать и мониторить все такие операции, ставить таймауты и лимиты памяти. - Минимизировать область видимости `eval` (использовать IIFE, очищать/обнулять переменные, избегать передачи глобальных объектов). - Не включать `unsafe-eval` в CSP, если можно избежать; если нужно — документировать и ограничивать домены/источники. Короткие рекомендации - Всегда используйте `JSON.parse` для JSON. - Для расширенных форматов — используйте специализированные парсеры (`json5`, `hjson` и т.п.). - Если видите `eval('('+json+')')` — замените немедленно на `JSON.parse` или безопасный парсер; рассматривайте это как серьёзную уязвимость при ревью кода. - Если есть обоснование для eval — перенести выполнение в изолированную песочницу/процесс и применить таймауты/ограничения. Если нужно — могу показать конкретные примеры замены `eval('('+json+')')` на `JSON.parse` и примеры безопасного использования `vm` в Node.js.
- Выполнение произвольного кода. Если строка `json` контролируется злоумышленником, она может содержать JavaScript выражения, которые будут выполнены. Пример простого трюка: если `json = "1); evil(); (1"`, то `eval('('+json+')')` выполнит `evil()`. Это основная и критическая уязвимость — удалённое выполнение кода (RCE).
- XSS и компрометация окружения. В браузере это может привести к XSS, краже cookies, сессий, токенов; на сервере — к утечке данных или удалённому выполнению команд.
- Обход CSP: использование eval требует разрешения `unsafe-eval` в CSP; его включение увеличивает атаку вектора.
- Производительность и отладка: `eval` медленнее, сложнее профилировать и безопасно отлаживать.
- Утечка лексического окружения / доступ к локальным переменным — `eval` видит внешние переменные (в некоторых режимах).
- Сложность безопасной валидации: попытки «очистить» строку regex-ами часто ненадёжны и легко обходятся.
Безопасные альтернативы
- Стандартный: `JSON.parse(json[, reviver])`. Надёжный, быстрый, не исполняет код. Использовать всегда, когда вход — валидный JSON.
- Для старых браузеров: подключить полифилл (напр., Douglas Crockford json2.js) или транспилировать/бандлить так, чтобы `JSON.parse` был доступен.
- Если нужен tolerant/lenient парсинг (одиночные кавычки, комментарии, незаключённые ключи): использовать специальные парсеры, которые безопасно превращают JS-объектную нотацию в структуру данных, например:
- tolerant JSON парсеры / libs: `json5`, `jju`, `hjson` — они парсят расширенные формы, но не выполняют код.
- потоковые парсеры для больших потоков: `clarinet`, `oboe`.
- Для сериализации/десериализации, включая функции/классы: использовать безопасные форматы и либы (например, `serialize-javascript` с осторожностью) либо явные трансформеры (replacer/reviver), а не eval.
- На Node.js при необходимости ограниченного выполнения: `vm` с контекстом и таймаутом — например `vm.runInNewContext('(' + json + ')', {}, {timeout: 1000})`. Это всё ещё выполняет код, но в ограниченном контексте; не равнозначно полной безопасной песочнице, требует осторожности.
Редкие случаи, когда eval может быть оправдан
- Вы реально парсите не JSON, а произвольную JS-нотацию (функции, выражения, RegExp, некорректный JSON), и вход полностью доверенный (локальные конфигурационные файлы, админский интерфейс). Даже тогда лучше использовать специализированный парсер.
- Инструменты разработки, REPL, отладочные/интерактивные среды, где пользователь ожидает выполнение JS и среда контролируется.
- Очень специфичные legacy-сценарии, где переписать зависимый код или заменить формат невозможно и вход стопроцентно доверен.
Меры минимизации риска если eval всё-таки используется
- Никогда не применять eval к данным из ненадёжных источников (внешние запросы, параметры URL, тело запроса, пользовательский ввод).
- Если возможна замена — заменить на `JSON.parse`.
- Если нельзя избежать выполнения кода:
- Выполнять в минимально привилегированном окружении: Node.js `vm.runInNewContext` с пустым контекстом и таймаутом.
- Использовать реальные песочницы/реалмы (SES от Agoric, изолированные процессы, контейнеры).
- Ограничить права процесса/контейнера (runtime user без прав, chroot/namespace, AppArmor/SELinux).
- Валидация входа регулярными грамматиками — только как дополнительная мера; не полагаться на простые regex-решения.
- Логировать и мониторить все такие операции, ставить таймауты и лимиты памяти.
- Минимизировать область видимости `eval` (использовать IIFE, очищать/обнулять переменные, избегать передачи глобальных объектов).
- Не включать `unsafe-eval` в CSP, если можно избежать; если нужно — документировать и ограничивать домены/источники.
Короткие рекомендации
- Всегда используйте `JSON.parse` для JSON.
- Для расширенных форматов — используйте специализированные парсеры (`json5`, `hjson` и т.п.).
- Если видите `eval('('+json+')')` — замените немедленно на `JSON.parse` или безопасный парсер; рассматривайте это как серьёзную уязвимость при ревью кода.
- Если есть обоснование для eval — перенести выполнение в изолированную песочницу/процесс и применить таймауты/ограничения.
Если нужно — могу показать конкретные примеры замены `eval('('+json+')')` на `JSON.parse` и примеры безопасного использования `vm` в Node.js.