'भरोसेमंद टाइप' की मदद से, DOM-आधारित क्रॉस-साइट स्क्रिप्टिंग के जोखिमों को रोकें

Krzysztof Kotowicz
Krzysztof Kotowicz

Browser Support

  • Chrome: 83.
  • Edge: 83.
  • Firefox: behind a flag.
  • Safari: 26.

Source

डीओएम पर आधारित क्रॉस-साइट स्क्रिप्टिंग (डीओएम XSS) तब होती है, जब उपयोगकर्ता के कंट्रोल वाले सोर्स (जैसे कि उपयोगकर्ता नाम या यूआरएल फ़्रैगमेंट से लिया गया रीडायरेक्ट यूआरएल) से डेटा, सिंक तक पहुंचता है. सिंक, eval() जैसे फ़ंक्शन या .innerHTML जैसे प्रॉपर्टी सेटर होते हैं, जो किसी भी JavaScript कोड को एक्ज़ीक्यूट कर सकते हैं.

डीओएम XSS, वेब सुरक्षा से जुड़ी सबसे आम कमज़ोरियों में से एक है. साथ ही, यह भी आम बात है कि डेवलपमेंट टीमें गलती से इसे अपने ऐप्लिकेशन में शामिल कर देती हैं. Trusted Types की मदद से, आपको ऐसे टूल मिलते हैं जिनसे ऐप्लिकेशन को डीओएम XSS की कमियों से सुरक्षित रखा जा सकता है. इसके लिए, आपको ऐप्लिकेशन लिखने और सुरक्षा की समीक्षा करने की सुविधा मिलती है. साथ ही, खतरनाक वेब एपीआई फ़ंक्शन को डिफ़ॉल्ट रूप से सुरक्षित बनाया जाता है. भरोसेमंद टाइप, उन ब्राउज़र के लिए पॉलीफ़िल के तौर पर उपलब्ध हैं जो अब तक इनका इस्तेमाल नहीं करते.

बैकग्राउंड

कई सालों से डीओएम एक्सएसएस, वेब सुरक्षा से जुड़ी सबसे आम और खतरनाक कमज़ोरियों में से एक है.

क्रॉस-साइट स्क्रिप्टिंग दो तरह की होती है. कुछ XSS कमज़ोरियां, सर्वर-साइड कोड की वजह से होती हैं. यह कोड, वेबसाइट बनाने के लिए असुरक्षित तरीके से एचटीएमएल कोड बनाता है. कुछ मामलों में, समस्या की मुख्य वजह क्लाइंट होती है. यहां JavaScript कोड, उपयोगकर्ता के कंट्रोल वाले कॉन्टेंट के साथ खतरनाक फ़ंक्शन कॉल करता है.

सर्वर साइड पर XSS को रोकने के लिए, स्ट्रिंग को जोड़कर एचटीएमएल जनरेट न करें. इसके बजाय, सुरक्षित कॉन्टेक्स्ट के हिसाब से अपने-आप एस्केप होने वाली टेंप्लेटिंग लाइब्रेरी का इस्तेमाल करें. साथ ही, बग को कम करने के लिए नॉनस पर आधारित कॉन्टेंट सुरक्षा नीति का इस्तेमाल करें.

अब ब्राउज़र भी Trusted Types का इस्तेमाल करके, क्लाइंट-साइड डीओएम पर आधारित XSS को रोकने में मदद कर सकते हैं.

एपीआई के बारे में जानकारी

भरोसेमंद टाइप, इन जोखिम भरे सिंक फ़ंक्शन को लॉक करके काम करते हैं. ऐसा हो सकता है कि आपने इनमें से कुछ के बारे में पहले से सुना हो. ऐसा इसलिए, क्योंकि ब्राउज़र वेंडर और वेब फ़्रेमवर्क सुरक्षा से जुड़ी वजहों से, आपको इन सुविधाओं का इस्तेमाल करने से पहले ही रोक देते हैं.

ट्रस्टेड टाइप के लिए, यह ज़रूरी है कि आप डेटा को इन सिंक फ़ंक्शन में पास करने से पहले प्रोसेस करें. सिर्फ़ स्ट्रिंग का इस्तेमाल करने पर, यह काम नहीं करता. इसकी वजह यह है कि ब्राउज़र को यह नहीं पता होता कि डेटा भरोसेमंद है या नहीं:

यह न करें
anElement.innerHTML  = location.href;
Trusted Types चालू होने पर, ब्राउज़र TypeError दिखाता है. साथ ही, स्ट्रिंग के साथ डीओएम XSS सिंक का इस्तेमाल करने से रोकता है.

यह दिखाने के लिए कि डेटा को सुरक्षित तरीके से प्रोसेस किया गया है, एक खास ऑब्जेक्ट बनाएं. इसे भरोसेमंद टाइप कहा जाता है.

यह करें
anElement.innerHTML = aTrustedHTML;
  
भरोसेमंद टाइप चालू होने पर, ब्राउज़र उन सिंक के लिए TrustedHTML ऑब्जेक्ट स्वीकार करता है जिनमें एचटीएमएल स्निपेट की ज़रूरत होती है. अन्य संवेदनशील सिंक के लिए, TrustedScript और TrustedScriptURL ऑब्जेक्ट भी मौजूद हैं.

Trusted Types की मदद से, आपके ऐप्लिकेशन के डीओएम XSS हमले की संभावना को काफ़ी हद तक कम किया जा सकता है. इससे सुरक्षा की समीक्षाएं आसान हो जाती हैं. साथ ही, यह आपको कंपाइल करते समय, लिंटिंग करते समय या अपने कोड को बंडल करते समय, टाइप के आधार पर सुरक्षा जांच लागू करने की सुविधा देता है. ये जांच, ब्राउज़र में रनटाइम के दौरान की जाती हैं.

भरोसेमंद टाइप इस्तेमाल करने का तरीका

कॉन्टेंट सुरक्षा नीति के उल्लंघन की रिपोर्ट पाने की तैयारी करना

ओपन-सोर्स reporting-api-processor या go-csp-collector जैसे रिपोर्ट कलेक्टर को डिप्लॉय किया जा सकता है. इसके अलावा, इनके जैसे किसी कमर्शियल रिपोर्ट कलेक्टर का इस्तेमाल किया जा सकता है. ReportingObserver का इस्तेमाल करके, ब्राउज़र में कस्टम लॉगिंग जोड़ी जा सकती है. साथ ही, उल्लंघन से जुड़ी गड़बड़ियों को डीबग किया जा सकता है:

const observer = new ReportingObserver((reports, observer) => {
    for (const report of reports) {
        if (report.type !== 'csp-violation' ||
            report.body.effectiveDirective !== 'require-trusted-types-for') {
            continue;
        }

        const violation = report.body;
        console.log('Trusted Types Violation:', violation);

        // ... (rest of your logging and reporting logic)
    }
}, { buffered: true });

observer.observe();

या इवेंट लिसनर जोड़कर:

document.addEventListener('securitypolicyviolation',
    console.error.bind(console));

सिर्फ़ रिपोर्ट करने वाला CSP हेडर जोड़ना

उन दस्तावेज़ों में यह एचटीटीपी रिस्पॉन्स हेडर जोड़ें जिन्हें आपको भरोसेमंद टाइप में माइग्रेट करना है:

Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example

अब सभी उल्लंघनों की सूचना //my-csp-endpoint.example को दी जाती है. हालांकि, वेबसाइट काम करती रहती है. अगले सेक्शन में, //my-csp-endpoint.example के काम करने के तरीके के बारे में बताया गया है.

Trusted Types के उल्लंघनों की पहचान करना

अब से, जब भी भरोसेमंद टाइप को किसी उल्लंघन का पता चलेगा, ब्राउज़र कॉन्फ़िगर किए गए report-uri को एक रिपोर्ट भेजेगा. उदाहरण के लिए, जब आपका ऐप्लिकेशन innerHTML को कोई स्ट्रिंग भेजता है, तो ब्राउज़र यह रिपोर्ट भेजता है:

{
"csp-report": {
    "document-uri": "https://my.url.example",
    "violated-directive": "require-trusted-types-for",
    "disposition": "report",
    "blocked-uri": "trusted-types-sink",
    "line-number": 39,
    "column-number": 12,
    "source-file": "https://my.url.example/script.js",
    "status-code": 0,
    "script-sample": "Element innerHTML <img src=x"
}
}

इससे पता चलता है कि लाइन 39 पर मौजूद https://my.url.example/script.js में, innerHTML को <img src=x से शुरू होने वाली स्ट्रिंग के साथ कॉल किया गया था. इस जानकारी से आपको यह पता लगाने में मदद मिलेगी कि कोड के किन हिस्सों में DOM XSS की समस्या हो सकती है और उनमें बदलाव करने की ज़रूरत है.

उल्लंघन ठीक करना

भरोसेमंद टाइप के उल्लंघन को ठीक करने के लिए, यहां दिए गए विकल्पों का इस्तेमाल करें. आपके पास ये विकल्प हैं: उल्लंघन करने वाले कोड को हटाएं, किसी लाइब्रेरी का इस्तेमाल करें, भरोसेमंद टाइप वाली नीति बनाएं या आखिरी विकल्प के तौर पर, डिफ़ॉल्ट नीति बनाएं.

उल्लंघन करने वाले कोड को फिर से लिखना

ऐसा हो सकता है कि अब नीति का पालन न करने वाले कोड की ज़रूरत न हो या उसे उन फ़ंक्शन के बिना फिर से लिखा जा सकता हो जिनकी वजह से उल्लंघन हुए हैं:

यह करें
el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);
यह न करें
el.innerHTML = '<img src=xyz.jpg>';

लाइब्रेरी का इस्तेमाल करना

कुछ लाइब्रेरी पहले से ही भरोसेमंद टाइप जनरेट करती हैं. इन्हें सिंक फ़ंक्शन में पास किया जा सकता है. उदाहरण के लिए, एचटीएमएल स्निपेट को सैनिटाइज़ करने के लिए, DOMPurify का इस्तेमाल किया जा सकता है. इससे XSS पेलोड हट जाते हैं.

import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});

DOMPurify भरोसेमंद टाइप के साथ काम करता है. यह TrustedHTML ऑब्जेक्ट में रैप किए गए सैनिटाइज़ किए गए एचटीएमएल को दिखाता है, ताकि ब्राउज़र उल्लंघन न करे.

भरोसेमंद टाइप की नीति बनाना

कभी-कभी, उल्लंघन करने वाले कोड को हटाया नहीं जा सकता. साथ ही, वैल्यू को सुरक्षित करने और आपके लिए भरोसेमंद टाइप बनाने के लिए कोई लाइब्रेरी उपलब्ध नहीं होती. ऐसे मामलों में, आपके पास खुद से भरोसेमंद टाइप ऑब्जेक्ट बनाने का विकल्प होता है.

सबसे पहले, नीति बनाएं. नीतियां, भरोसेमंद टाइप के लिए फ़ैक्ट्रियां होती हैं. ये अपने इनपुट पर कुछ सुरक्षा नियमों को लागू करती हैं:

if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
  const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
    createHTML: string => string.replace(/\</g, '&lt;')
  });
}

यह कोड, myEscapePolicy नाम की एक नीति बनाता है. यह नीति, createHTML() फ़ंक्शन का इस्तेमाल करके TrustedHTML ऑब्जेक्ट बना सकती है. तय किए गए नियम, < वर्णों को एचटीएमएल-एस्केप करते हैं, ताकि नए एचटीएमएल एलिमेंट न बनाए जा सकें.

नीति का इस्तेमाल इस तरह करें:

const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML);  // true
el.innerHTML = escaped;  // '&lt;img src=x onerror=alert(1)>'

डिफ़ॉल्ट नीति का इस्तेमाल करना

कभी-कभी उल्लंघन करने वाले कोड को बदला नहीं जा सकता. उदाहरण के लिए, अगर किसी सीडीएन से तीसरे पक्ष की लाइब्रेरी लोड की जा रही है. ऐसे में, डिफ़ॉल्ट नीति का इस्तेमाल करें:

if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
  trustedTypes.createPolicy('default', {
    createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
  });
}

default नाम की नीति का इस्तेमाल हर उस जगह पर किया जाता है जहां सिंक में स्ट्रिंग का इस्तेमाल किया जाता है. हालांकि, यह सिर्फ़ भरोसेमंद टाइप को स्वीकार करता है.

कॉन्टेंट की सुरक्षा के लिए बनी नीति को लागू करने पर स्विच करना

जब आपका ऐप्लिकेशन अब उल्लंघन नहीं करता है, तब भरोसेमंद टाइप लागू किए जा सकते हैं:

Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example

अब आपका वेब ऐप्लिकेशन कितना भी जटिल क्यों न हो, सिर्फ़ आपकी किसी नीति में मौजूद कोड की वजह से डीओएम एक्सएसएस की कमज़ोरी आ सकती है. साथ ही, नीति बनाने की सुविधा को सीमित करके, इसे और भी ज़्यादा सुरक्षित किया जा सकता है.

इस बारे में और पढ़ें