flawopen.com/Загрязнение прототипа/JSON.parse()
Вызывает ли JSON.parse() загрязнение прототипа?
Нет. JSON.parse() создаёт собственное свойство данных с именем __proto__; разбор не вызывает унаследованный setter и не меняет прототип. Риск возникает позже, если код копирует этот ключ через setter, рекурсивно объединяет недоверенные ключи или записывает их по контролируемому атакующим пути объекта.
// JSON.parse создаёт данные, но не загрязняет прототип. const input = '{"__proto__":{"isAdmin":true}}'; const parsed = JSON.parse(input); // Object.assign использует Set для цели. Здесь меняется только // прототип этого локального демонстрационного объекта. const copied = Object.assign({}, parsed); Object.getPrototypeOf(copied).isAdmin; // true Object.prototype.isAdmin; // undefined
// Разберите данные и создайте только разрешённые поля. const input = '{"theme":"dark","locale":"en","__proto__":{"isAdmin":true}}'; const parsed = JSON.parse(input); const config = { theme: parsed.theme, locale: parsed.locale, }; // Для недоверенных ключей словаря избегайте унаследованных setter. const options = Object.assign(Object.create(null), parsed);
Почему JSON.parse() не является источником загрязнения
Ключ JSON и setter прототипа в литерале объекта — разные операции. Разбор JSON определяет собственные свойства нового значения и не выполняет obj.__proto__ = value. Последующие операции могут работать иначе: Object.assign(target, source) присваивает свойства цели, поэтому собственный ключ __proto__ может вызвать унаследованный setter цели. Это меняет прототип целевого объекта, но само по себе не изменяет Object.prototype.
После const o = JSON.parse('{"__proto__":{"a":1}}') выражение Object.hasOwn(o, "__proto__") истинно, а Object.prototype.a остаётся неопределённым. Последующее копирование этого ключа в обычную цель может изменить её прототип. Spread объектов создаёт собственные свойства и не вызывает setter, но не защищает от небезопасного рекурсивного слияния или записи по пути.
Где возникает реальный риск
Разобранный JSON — недоверенные данные, а не загрязнённый прототип. Риск зависит от последующих операций записи: рекурсивного merge, setter пути или цикла копирования с присваиванием свойств обычному объекту. Уязвимое рекурсивное слияние может пройти через __proto__ или constructor.prototype к общему прототипу; копирование одного уровня может изменить только цель. Перед оценкой воздействия изучите конкретную функцию и объект назначения.
Проверяйте места записи недоверенных ключей, а не только вызовы JSON-парсера: глубокие merge-функции, setter путей и циклы for...in с присваиванием. Отклоняйте __proto__, constructor и prototype, если ключами управляет пользователь; предпочитайте проверенную схему известной формы или словарь без прототипа.
Частые вопросы
Reviver может удалить этот ключ при разборе, если приложению он не нужен. Это дополнительная защита, но она не заменяет исправление небезопасных слияний и setter путей; важны также constructor.prototype и источники не из JSON.
Разбор тела запроса создаёт значение JavaScript, но не делает безопасными последующие копирования и слияния. Проверьте конкретный парсер и дальнейшие операции. Некоторые парсеры строк запроса строят вложенные объекты из скобочной нотации — это отдельный источник ввода.
Нет. Сериализация читает значения и создаёт текст JSON; она не меняет прототип. Риск может появиться снова, когда этот текст разбирают и передают небезопасной функции.