एक लर्निंग प्लॅटफॉर्म जो विद्यार्थ्यांना MongoDB क्वेरीज तयार करण्याची परवानगी देतो, त्याने आता Node.js चा vm मॉड्यूल वापरणे बंद केले आहे आणि आता प्रत्येक क्वेरी कार्यान्वित (execute) करण्यापूर्वी तिचे abstract syntax tree (AST) मध्ये रूपांतर (parse) करतो. या बदलामुळे तो सँडबॉक्स (sandbox) काढून टाकला आहे जो तोडता येऊ शकत होता, ज्यामुळे बॅकएंडला चुकीच्या पद्धतीने तयार केलेल्या स्ट्रिंगद्वारे इंजेक्ट होणाऱ्या कोणत्याही अनधिकृत कोडपासून (arbitrary code) संरक्षण मिळते.

मूळ सँडबॉक्स का अयशस्वी ठरला

पहिल्या अंमलबजावणीमध्ये (implementation) एका लाईव्ह डेटाबेस हँडलला vm.runInContext कॉलमध्ये गुंडाळले होते आणि regular expression वापरून वापरकर्त्यांवर निर्बंध आणण्याचा प्रयत्न केला होता. यामध्ये केवळ find किंवा aggregate सारख्या मेथड नावांना परवानगी होती; इतर सर्व आयडेंटिफायर्स (identifiers) फिल्टर केले जावेत अशी अपेक्षा होती.

दोन त्रुटींमुळे तो दृष्टिकोन असुरक्षित ठरला:

  • Regex फिल्टरिंग बायपास करता येते. JavaScript मध्ये कोड कोणत्याही प्रॉपर्टीपर्यंत पोहोचण्यासाठी ब्रॅकेट नोटेशन (obj["constructor"]) वापरू शकतो. एखादा अटॅकर Function कन्स्ट्रक्टर मिळवू शकतो, नवीन फंक्शन तयार करू शकतो आणि त्यांना हवा तो कोणताही कोड रन करू शकतो. मूळ सोर्स असंख्य प्रकारे पुन्हा लिहिला जाऊ शकत असल्यामुळे, regex ला त्यामागील प्रॉपर्टी ॲक्सेस कधीच दिसत नाही.
  • vm ही सुरक्षा सीमा (security boundary) नाही. Node च्या डॉक्युमेंटेशननुसार, vm केवळ global ऑब्जेक्टला वेगळे (isolate) करते, संपूर्ण प्रोसेसला नाही. सँडबॉक्समध्ये लाईव्ह डेटाबेस कनेक्शन इंजेक्ट केल्यामुळे, त्या कॉन्टेक्स्टमधील कोडकडे त्या कनेक्शनवरील कोणतीही मेथड कॉल करण्याची क्षमता कायम राहते, ज्यामध्ये डेटा लिहिणाऱ्या किंवा डिलीट करणाऱ्या मेथड्सचा समावेश असतो. सँडबॉक्सने कोडला होस्ट प्रोसेसवर परिणाम करण्यापासून रोखले नाही.

AST-आधारित उपाय

टीमने कोड एक्झिक्यूशनच्या जागी स्टॅटिक अनालिसिस (static analysis) वापरले आहे. आता क्वेरी स्ट्रिंग्स acorn पार्सरला पाठवल्या जातात, जो एक AST तयार करतो—जो कोडच्या सिंटॅक्टिक स्ट्रक्चरचे (syntactic structure) ट्री रिप्रेझेंटेशन आहे. या AST ची तपासणी एका कडक व्हाईटलिस्टच्या (whitelist) आधारे नोड-बाय-नोड केली जाते:

  • Literals, arrays आणि objects ला केवळ साध्या व्हॅल्यूज (plain values) म्हणून वापरल्यास परवानगी दिली जाते.
  • Method calls एका पूर्व-निर्धारित संचापुरती (find, sort, limit, इ.) मर्यादित आहेत. इतर कोणताही कॉल नाकारला जातो.
  • Computed property access (उदा. obj[expr]) किंवा स्पष्टपणे सूचीबद्ध नसलेला कोणताही नोड प्रकार आढळल्यास त्वरित एरर (failure) येतो.

पार्सर कच्च्या मजकुरावर (raw text) नाही तर ट्रीवर काम करत असल्यामुळे, पर्यायी स्पेलिंग किंवा ब्रॅकेट-नोटेशनच्या युक्त्यांनी त्याला फसवता येत नाही. एखादी कन्स्ट्रक्टर चेन जी regex ला चकवा देऊ शकली असती, ती एक न ओळखल्या गेलेला नोड म्हणून दिसते आणि कोणताही कोड रन होण्यापूर्वीच नाकारली जाते.

सुरक्षेसाठी याचा अर्थ काय?

नवीन डिझाइन "deny by default" (बाय डिफॉल्ट नाकारणे) या तत्त्वाचे पालन करते:

  • काय परवानगी आहे ते ठरवा, काय निषिद्ध आहे ते नाही. टेक्स्ट-आधारित अलाऊ-लिस्टिंग (allow-listing) सर्वसमावेशक असू शकत नाही; परंतु AST मध्ये नोड प्रकारांचा एक मर्यादित संच असतो, ज्यामुळे सर्वसमावेशक तपासणी करणे शक्य होते.
  • सँडबॉक्समध्ये कधीही लाईव्ह रिसोर्सेस उघडे (expose) करू नका. आयसोलेटेड कॉन्टेक्स्टमध्ये डेटाबेस हँडल पास केल्यामुळे सँडबॉक्समधील कोडला बॅकएंडशी थेट संपर्क मिळतो. पार्सर पद्धतीत युजर कोडला कधीही लाईव्ह ऑब्जेक्ट दिला जात नाही; ती केवळ क्वेरीचा हेतू (intent) काढते.
  • स्ट्रक्चरची पडताळणी करा आणि नंतर सुरक्षितपणे एक्झिक्युट करा. एकदा का AST व्हॅलिडेशन पूर्ण झाले की, प्लॅटफॉर्म स्वतःच्या विश्वसनीय कोड पाथचा वापर करून परवानगी दिलेल्या कॉल्सचे रूपांतर प्रत्यक्ष MongoDB ड्रायव्हर मेथड्समध्ये करते.

पुढे काय पाहावे (किंवा लक्ष द्यावे)

  • तुमच्या कोडबेसमध्ये eval, new Function, किंवा vm चा कोणताही वापर असल्यास त्याची ऑडिट (audit) करा. व्हिटलिस्ट असूनही JavaScript च्या डायनॅमिक स्वरूपामुळे ती धोक्यात येऊ शकते.
  • जिथे शक्य असेल तिथे युजर-जनरेटेड कोडसाठी AST पार्सिंगचा अवलंब करा. acorn, esprima, किंवा babel-parser सारखी लायब्ररी हे रूपांतर सोपे करतात.
  • लाईव्ह ऑब्जेक्ट्सचा उघडा वापर मर्यादित करा. जर डेटाबेस कनेक्शन, फाईल हँडल किंवा नेटवर्क सॉकेट उपलब्ध असणे आवश्यक असेल, तर त्याला अशा प्रॉक्सीमध्ये (proxy) गुंडाळा जी केवळ तुम्ही परवानगी देऊ इच्छित असलेल्या किमान मेथड्सच उघड्या पाडेल.
  • एज केसेसचे (edge cases) टेस्टिंग ऑटोमेट करा. तुमचा पार्सर त्यांना नाकारतो की नाही हे तपासण्यासाठी ब्रॅकेट नोटेशन, कम्प्युटेड कीज किंवा प्रोटोटाइप मॅनिप्युलेशन वापरणाऱ्या क्वेरीज तयार करा.

सारांश

सँडबॉक्स म्हणून vm.runInContext वर अवलंबून राहणे सुरक्षेचा खोटा आभास देते; regex फिल्टर JavaScript च्या लवचिक सिंटॅक्सला कव्हर करू शकत नाहीत आणि सँडबॉक्स प्रोसेस रिसोर्सेसना आयसोलेट करत नाही. युजर इनपुटचे AST मध्ये रूपांतर करणे आणि केवळ तुम्हाला समजणाऱ्या नोड्सना व्हाईटलिस्ट करणे, एक ठोस आणि मेंटेनेबल अडथळा निर्माण करते जो कोणताही घातक कोड रन होण्यापूर्वीच थांबवतो. जर तुमचा प्लॅटफॉर्म वापरकर्त्यांना कोड लिहिण्याची परवानगी देत असेल, तर आजच eval-शैलीतील एक्झिक्यूशनच्या जागी स्टॅटिक अनालिसिस वापरा.