FlawOpen

flawopen.com/Prototype Pollution/Object.assign()

क्या Object.assign() Prototype Pollution से सुरक्षित है?

CWE-1321: Prototype Pollutionसंदर्भ पृष्ठ
संक्षिप्त उत्तर

पूरी तरह नहीं। Object.assign() source की अपनी enumerable properties को target पर assign करके कॉपी करता है। यदि source में अपनी enumerable __proto__ key हो, तो assignment inherited setter को चला सकता है और target object का prototype बदल सकता है। केवल shallow assign से साझा Object.prototype नहीं बदलता; recursive merge का प्रभाव अधिक व्यापक हो सकता है।

असुरक्षित — assign और recursive merge
// A JSON own "__proto__" key reaches the inherited setter
const source = JSON.parse('{"__proto__":{"isAdmin":true}}');
const target = Object.assign({}, source);
// target's prototype changes; Object.prototype does not.

// Recursive merge can reach Object.prototype
function deepAssign(target, source) {
  for (const key of Object.keys(source)) {
    if (source[key] && typeof source[key] === 'object') {
      target[key] = target[key] || {};
      deepAssign(target[key], source[key]);
      // With key === '__proto__', target[key] can be
      // Object.prototype; recursion then writes into it.
    } else {
      Object.assign(target, { [key]: source[key] });
    }
  }
}
सुरक्षित — shallow copy के लिए spread; recursive merge को guard करें
// Spread creates an own data property; it does not invoke the setter.
const shallow = { ...userInput };

// Reject dangerous keys at every depth and use own data properties.
const blocked = new Set(['__proto__', 'constructor', 'prototype']);
function isRecord(value) {
  if (value === null || typeof value !== 'object' || Array.isArray(value)) return false;
  const proto = Object.getPrototypeOf(value);
  return proto === Object.prototype || proto === null;
}
function copySafe(value) {
  if (Array.isArray(value)) return value.map(copySafe);
  return isRecord(value) ? mergeSafe(value) : value;
}
function mergeSafe(source, target = Object.create(null)) {
  if (!isRecord(source)) throw new TypeError('Expected a plain object');
  for (const key of Object.keys(source)) {
    if (blocked.has(key)) continue;
    const value = source[key];
    const current = Object.hasOwn(target, key) ? target[key] : undefined;
    const next = isRecord(value)
      ? mergeSafe(value, isRecord(current) ? current : Object.create(null))
      : copySafe(value);
    Object.defineProperty(target, key, {
      value: next, enumerable: true, writable: true, configurable: true
    });
  }
  return target;
}

__proto__ key पर Object.assign() का व्यवहार

Object.assign() target के सामान्य property-setting व्यवहार से source की हर own enumerable property कॉपी करता है। यदि JSON से बने source में own __proto__ property हो और target inherited पुराने setter को प्राप्त करता हो, तो setter target का [[Prototype]] बदल देता है। इस स्थिति में literal own __proto__ property नहीं बनती।

Object spread ({ ...source }) परिणाम में data property बनाता है; वही key own data property बनती है और inherited setter नहीं चलाती। shallow copy के लिए यह उपयोगी अंतर है, लेकिन बाद के recursive merge से सामान्य सुरक्षा नहीं।

Recursive merge साझा prototypes को कैसे प्रदूषित कर सकता है

असुरक्षित recursive code जब attacker-controlled keys का अनुसरण करके साझा prototype में लिखता है, तब Prototype Pollution होता है। आम pattern nested object खोजने के लिए target[key] पढ़ता है। key यदि __proto__ हो, तो यह Object.prototype लौटा सकता है; अगला recursive write सामान्य objects को व्यापक रूप से प्रभावित कर सकता है। constructor.prototype जैसे रास्ते भी जाँचें।

Review में attacker-controlled keys से target पढ़ने या लिखने वाले operations ढूँढें, देखें कि क्या वे setters चलाते हैं, और सुनिश्चित करें कि हर recursive स्तर खतरनाक keys अस्वीकार करे।

अक्सर पूछे जाने वाले प्रश्न

क्या object spread ({ ...obj }) भी उतना ही सुरक्षित है?

Shallow copy के लिए हाँ: spread own data property बनाता है और inherited setter नहीं चलाता। इसके विपरीत, Object.assign() enumerable source key __proto__ मिलने पर target prototype बदल सकता है। इनमें से कोई भी shallow operation अकेले साझा Object.prototype नहीं बदलता।

structuredClone() का क्या?

structuredClone() अलग copy बनाता है और source की prototype chain लागू नहीं करता। own __proto__ data key copy में data ही रहती है; बाद का असुरक्षित merge फिर भी उसे विशेष रूप से समझ सकता है।

क्या बस कोई library इस्तेमाल करूँ?

ऐसी merge library चुनें जिसका रखरखाव और अपडेट जारी हो; हर nesting स्तर पर __proto__, constructor और prototype के व्यवहार की जाँच करें। कई लोकप्रिय merge और object-path utilities के लिए Prototype Pollution advisories आई हैं; fix केवल patched versions में होता है।

संदर्भ

यह भी देखें JavaScript प्रोटोटाइप प्रदूषण: कारण और बचाव क्या JSON.parse() प्रोटोटाइप प्रदूषण करता है? क्या JSON.parse(), eval() से सुरक्षित है? क्या lodash.merge() prototype pollution से सुरक्षित है?