
TC39 प्रोटोटाइप प्रदूषण को कम करने का प्रस्ताव
लेखक: Santiago Díaz (Google)
चैंपियन: Shu-yu Guo (Google)
चरण: 1
freeze, seal और preventExtensions से जुड़ी समस्याएँ
यह प्रस्ताव एक भाषा-स्तरीय भेद्यता, जिसे प्रोटोटाइप पोल्यूशन के रूप में जाना जाता है, को एक ऐसे तंत्र से कम करना चाहता है जो फ्रीज़ प्रिमिटिव का पूरक हो, साथ ही एक ऐसा तंत्र जो अधिकांश कोड बेस को इसके अनुकूल बना सके। यह एक ऑप्ट-इन सुविधा का वर्णन करता है जो प्रोटोटाइप को केवल रिफ्लेक्शन APIs के माध्यम से उपलब्ध कराती है। ऐसा करने से, obj[key] कथन अब प्रोटोटाइप तक नहीं पहुँच सकता। इस सुविधा के अनुकूल कोड बेस प्रोटोटाइप का उपयोग करने के तरीके में अधिक इरादतन होते हैं।
PP भेद्यताएँ हमलावरों को उन ऑब्जेक्ट में हेरफेर करने की अनुमति देती हैं जिन्हें वे नियंत्रित नहीं करते या जिन तक उनकी रनटाइम पर पहुँच नहीं होती। यह 'दूरी पर रहस्यमय प्रभाव' प्रिमिटिव अन्य ऑब्जेक्ट का आकार बदलने और उनके गुणों को ओवरराइड करने के लिए इस्तेमाल किया जा सकता है, जिससे रनटाइम में ऑब्जेक्ट दूषित हो जाते हैं।
दूषित ऑब्जेक्ट उस कोड की अंतर्निहित मान्यताओं को अमान्य कर देते हैं जो अन्यथा सुरक्षित/सही होता, और JS कोड बेस में मनमाना कोड निष्पादन और कई अन्य सुरक्षा समस्याएँ पैदा कर सकते हैं। प्रोटोटाइप पोल्यूशन बग अक्सर वेब अनुप्रयोगों में प्रकट होते हैं, लेकिन यह गैर-वेब JS रनटाइम को भी प्रभावित करते हैं।
JS में ऑब्जेक्ट गुण किसी भी कोड द्वारा लिखने योग्य होते हैं जो उन्हें संदर्भित कर सकता है। विशेष रूप से, यदि कई ऑब्जेक्ट किसी साझा गुण पर निर्भर करते हैं, तो उनमें से कोई भी अन्य सभी पर परिवर्तन प्रभावी कर सकता है।
PP की एक विशेष विशेषता यह है कि यह एक केवल-डेटा हमला है, जो पूरी तरह से डेटा के माध्यम से कोड निष्पादन प्राप्त करने की अनुमति देता है। उदाहरण के लिए, निम्नलिखित भेद्य कोड और उसका संगत एक्सप्लॉइट देखें:
// source is attacker-controlled
function merge(target, source) {
for (let key in source) {
if (typeof source[key] === 'object') {
if(target[key] === undefined) {
target[key] = {};
}
target[key] = merge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
// User input comes as a string
const userSuppliedObj = JSON.parse('{"__proto__": {"polluted": true}}');
// Trigger prototype pollution
merge({}, userSuppliedObj);
// Create a brand new object
const newObj = {};
// Has polluted property
console.log(newObj.polluted); // true
ध्यान दें कि यह एक्सप्लॉइट बिना कोई बाहरी कोड इंजेक्ट किए नए ऑब्जेक्ट के निर्माण को दूषित करने में सक्षम है।
इस विशेष गुण के कारण, कोड निष्पादन समस्याओं के विरुद्ध आधुनिक शमन उपाय - जैसे Content Security Policy या Trusted Types - PP से सुरक्षा में कम पड़ जाते हैं, क्योंकि वे कोड उत्पत्ति को लागू करने पर केंद्रित होते हैं।
ध्यान दें कि केवल-डेटा हमले उन स्थितियों में प्रासंगिक हैं जहाँ VM पर चलने वाला कोड विश्वसनीय है और मनमाना कोड निष्पादन का सुरक्षा पर प्रभाव पड़ता है।
freeze, seal और preventExtensions से जुड़ी समस्याएँमौजूदा फ्रीज़िंग प्रिमिटिव में महत्वपूर्ण डिज़ाइन समस्याएँ हैं जो उन्हें व्यापक रूप से अपनाए जाने की संभावना कम बनाती हैं। वे विशेषज्ञ उपयोगकर्ताओं के लिए उपयोगी हो सकते हैं, लेकिन अधिकांश डेवलपर्स द्वारा तैनात किए जाने के लिए उपयुक्त नहीं हैं, जो उचित रूप से प्रोटोटाइप के परिवर्तनशील (mutable) होने की अपेक्षा करते हैं:
फ्रीज़ APIs ओवरराइड मिस्टेक और अन्य असंगतियों से ग्रस्त हैं, जो मौजूदा कोड बेस में बग पैदा करती हैं, जिससे वे थ्रो करते हैं या इससे बुरा, स्लॉपी मोड में चुपचाप विफल हो जाते हैं। ओवरराइड मिस्टेक की पिछली जाँच ने निष्कर्ष निकाला था कि यह स्ट्रिक्ट मोड में ~10% कोड बेस और स्लॉपी मोड में 20% पर ट्रिगर होता है। यह जाँच कुछ समय बाद बंद कर दी गई थी।
फ्रीज़ APIs डेवलपर्स को यह जानने की भारी ज़िम्मेदारी देती हैं कि सुरक्षित कोड बेस बनाए रखने के लिए कौन से प्रोटोटाइप फ्रीज़ किए जाने चाहिए, यह मानते हुए कि डेवलपर्स सुरक्षा विशेषज्ञ हैं। ये APIs सुरक्षा का क्या (what) वर्णन करती हैं, लेकिन कैसे (how) नहीं। Object को फ्रीज़ करना निश्चित रूप से पर्याप्त नहीं है, क्योंकि कई एक्सप्लॉइट Array का दुरुपयोग करते हैं। Error, Date, Reflect या Proxy का क्या? या भविष्य के अंतर्निहित प्रकार? फ्रीज़ APIs इन सवालों का कोई उत्तर नहीं देती हैं।
फ्रीज़ APIs एक स्थिर फ्रीज़िंग बिंदु मान लेती हैं: रनटाइम पर एक निश्चित क्षण जब प्रोटोटाइप स्थिर हो चुके होते हैं और फ्रीज़ किए जा सकते हैं। व्यवहार में, यह बिंदु अस्थिर होता है और सक्रिय रूप से विकसित होने वाले कोड बेस में समय के साथ बदलता रहता है। हालाँकि आज कई अनुप्रयोगों में ऐसा बिंदु पाया जा सकता है, नई निर्भरताएँ, पॉलीफिल, कोड संरचना में बदलाव और हॉटस्वैपिंग तथा डेवलपर टूल जैसी उन्नत सुविधाएँ फ्रीज़िंग बिंदुओं को एक गतिशील लक्ष्य बना देती हैं।
फ्रीज़ APIs पूरी प्रोटोटाइप श्रृंखला की रक्षा नहीं कर सकतीं। JS में, ऑब्जेक्ट को किसी भी समय प्रोटोटाइप श्रृंखला में जोड़ा या हटाया जा सकता है। पूरी श्रृंखला की सुरक्षा के लिए, किसी को श्रृंखला में जोड़े गए ऑब्जेक्ट को फ्रीज़ करना हमेशा याद रखना चाहिए, जो एक त्रुटि-प्रवण प्रक्रिया है। जब वे श्रृंखला से हटा दिए जाते हैं, तो उन्हें फिर से अनफ्रीज़ नहीं किया जा सकता।
संक्षेप में: एक ऐसी सुविधा जो प्रोटोटाइप को केवल रिफ्लेक्शन APIs के लिए उजागर करती है। यदि प्रोटोटाइप __proto__ या prototype जैसे गुणों के माध्यम से उपलब्ध नहीं कराए जाते, तो वे केवल-डेटा समस्याओं के संपर्क में नहीं आते।
इसे एक उदाहरण से बेहतर समझा जा सकता है: obj[one][two] = value कथन obj.__proto__.polluted के माध्यम से PP के प्रति भेद्य है। यदि कोई Object.prototype.__proto__ गुण को हटा देता है, तो वही कथन अब भेद्य नहीं रहता, क्योंकि यह प्रोटोटाइप तक पहुँचने के एकमात्र अन्य तरीके में फिट नहीं बैठ सकता, जो obj.constructor.prototype.polluted है। ध्यान दें कि प्रोटोटाइप को हटाया नहीं जा सकता।
इस प्रस्ताव को रिफ्लेक्शन APIs प्रदान करके और एक नई ऑप्ट-इन एनकैप्सुलेशन सुविधा बनाकर लागू किया जा सकता है जो प्रोटोटाइप गुणों को हटा देती है। प्रत्येक चरण का विवरण निम्नलिखित है।
__proto__ एक विरासत (legacy) गुण नाम है जिसे हटाया जा सकता है, लेकिन इसके पीछे का आंतरिक स्लॉट Object/Reflect.getPrototypeOf के माध्यम से पढ़ा जा सकता है और Object/Reflect.setPrototypeOf के माध्यम से लिखा जा सकता है, जो इस गुण को पहले से चल रहे कोड के लिए सुलभ बनाता रहेगा।
हम prototype के लिए नए APIs बनाने का प्रस्ताव करते हैं, उदाहरण के लिए getClassPrototypeOf और setClassPrototypeOf, जो इस गुण नाम को बिना किसी भी तरह से बदले हटाने की अनुमति देंगे कि यह विशेष गुण कैसे काम करता है और VM को कैसे समर्थन देता है।
रिफ्लेक्शन APIs को पॉलीफिल किया जा सकता है, जो कठोर (hardened) कोड बेस को पुराने संस्करणों सहित सभी ब्राउज़रों में काम करने की अनुमति देता है।