RamenHire जॉब बोर्ड में एक 'स्टोर्ड HTML इंजेक्शन' (stored HTML injection) बग के कारण कोई भी सार्वजनिक फॉर्म भर सकता था और टीम के नोटिफिकेशन ईमेल को फिशिंग प्लेटफॉर्म में बदल सकता था।
यह बग कैसे आया
RamenHire, Next.js और Supabase पर चलता है और स्टार्टअप्स को बिना लॉग-इन किए जॉब पोस्ट करने की सुविधा देता है। प्रत्येक सबमिशन (submission) इंटरनल हायरिंग टीम को एक ईमेल भेजता है। ईमेल की बॉडी को सीधे HTML स्ट्रिंग में कच्चे फॉर्म फ़ील्ड्स—कंपनी का नाम, जॉब डिस्क्रिप्शन, कॉन्टैक्ट नेम—को जोड़कर बनाया जाता है। मेल सर्विस तक पहुँचने से पहले किसी भी तरह की एस्केपिंग (escaping) या सैनिटाइजेशन (sanitisation) नहीं की जाती है।
क्योंकि टेम्पलेट यूजर इनपुट को सुरक्षित HTML मानता है, इसलिए एक दुर्भावनापूर्ण (malicious) आवेदक एक फर्जी "क्लिक हियर टू वेरीफाई" (click here to verify) लिंक इंजेक्ट कर सकता है। यह पेलोड (payload) जॉब लिस्टिंग के हिस्से के रूप में सर्वर पर स्टोर हो जाता है, जिससे बाद में भेजे जाने वाले हर नोटिफिकेशन ईमेल में वह दुर्भावनापूर्ण कोड आ जाता है। एक हमलावर उस लिंक को असली "View Dashboard" बटन के बगल में छिपा सकता है, जिससे टीम का कोई सदस्य क्रेडेंशियल्स (credentials) उजागर कर दे या किसी फिशिंग साइट पर चला जाए।
यह महत्वपूर्ण क्यों था
ये ईमेल मुख्य हायरिंग टीम को जाते हैं, जो उम्मीदवारों को पाइपलाइन में आगे बढ़ाने के लिए लगातार लिंक पर क्लिक करते रहते हैं।
तकनीकी कमी
HTML एस्केपिंग कोई एक स्टेप नहीं है। दो कॉन्टेक्स्ट (contexts) को सुरक्षा की आवश्यकता होती है:
- Text nodes – टैग्स के बीच की सामग्री।
<को<और>को>में बदलने से टैग्स तो रुक जाते हैं, लेकिन यह हमलावर को एट्रिब्यूट वैल्यू (attribute value) से बाहर निकलने से नहीं रोकता है। - Attribute values – टैग्स के अंदर की स्ट्रिंग्स जैसे कि
href="…"। एक कोट ("या') इंजेक्ट करने से हमलावर एट्रिब्यूट को समय से पहले बंद कर सकता है और अपना खुद का मार्कअप डाल सकता है।
मूल कोड केवल प्लेन टेक्स्ट को हैंडल करता था, जिससे एट्रिब्यूट इंजेक्शन का रास्ता पूरी तरह खुला रह गया।
समस्या का समाधान
डेवलपर ने एक छोटा escapeHtml हेल्पर जोड़ा और इंटरपोलेशन (interpolation) से पहले यूजर द्वारा दिए गए हर वैल्यू पर इसे लागू किया, चाहे वह वैल्यू टेक्स्ट नोड में जाए या एट्रिब्यूट में।
वेरिफिकेशन स्टेप्स
- Local testing – टेम्पलेट फंक्शन्स में इंजेक्शन स्ट्रिंग्स का एक सेट डाला गया। प्रत्येक टेस्ट में केवल एस्केप किए गए कैरेक्टर्स दिखाई दिए, कोई भी एक्जीक्यूटेबल (executable) टैग नहीं।
- Production testing – पैच तैनात करने के बाद, लाइव साइट के माध्यम से एक पेलोड सबमिट किया गया, जिसने Cloudflare Turnstile बॉट चेक को पास कर लिया। परिणामी ईमेल डेवलपर के इनबॉक्स में पहुंचा; दुर्भावनापूर्ण लिंक और कोई भी स्क्रिप्ट टैग गार्बल्ड (garbled) टेक्स्ट के रूप में दिखाई दिए, क्लिक करने योग्य एलिमेंट्स के रूप में नहीं।
Gmail के बारे में एक नोट: सर्विस अभी भी URL को नीला रंग दे सकती है और उसे क्लिक करने योग्य बना सकती है, लेकिन यह क्लाइंट-साइड की सुविधा है और इसका मतलब यह नहीं है कि HTML इंजेक्शन सफल रहा। यह फिक्स इंजेक्शन को असली बटन बनाने या स्क्रिप्ट चलाने से रोकता है।
डेवलपर्स को किन बातों का ध्यान रखना चाहिए
- सार्वजनिक फॉर्म से मिलने वाले डेटा पर कभी भरोसा न करें – मासूम दिखने वाले फॉर्म भी ऐसा स्टोर्ड आउटपुट दे सकते हैं जिसका उपयोग कहीं और किया जा सके।
- उपयोग के समय एस्केप करें – रेंडरिंग (rendering) से ठीक पहले कॉन्टेक्स्ट-अवेयर एस्केपिंग (टेक्स्ट बनाम एट्रिब्यूट) लागू करें, न कि पाइपलाइन में पहले।
- क्लाइंट-साइड और सर्वर-साइड दोनों का परीक्षण करें – यूनिट टेस्ट स्पष्ट विफलताओं को पकड़ लेते हैं; लाइव UI के माध्यम से एक मैनुअल एंड-टू-एंड टेस्ट यह पुष्टि करता है कि वास्तविक ट्रैफिक के दौरान सुरक्षा बनी रहती है।
- ईमेल क्लाइंट की विचित्रताओं (quirks) के प्रति सचेत रहें – कुछ क्लाइंट्स URL को ऑटो-लिंक कर देते हैं, जिससे सुरक्षा का झूठा अहसास होता है। यह सत्यापित करें कि अंतर्निहित (underlying) HTML में कोई सक्रिय एलिमेंट न हो।
निष्कर्ष
यूजर इनपुट को संभालने में एक छोटी सी चूक ने नियमित नोटिफिकेशन ईमेल को फिशिंग वेक्टर में बदल दिया। एक व्यापक HTML-एस्केपिंग रूटीन पेश करने और लोकल व प्रोडक्शन में फिक्स को सत्यापित करने से इनबॉक्स की अखंडता (integrity) बहाल हो गई। यह घटना किसी भी ऐसे वेब ऐप के लिए एक शाश्वत सबक है जो अविश्वसनीय डेटा से HTML जेनरेट करता है: उचित सैनिटाइजेशन अनिवार्य है, और इसे अनदेखा करना आपके यूजर्स के इनबॉक्स तक सीधा रास्ता खोल सकता है।
