Sanitizer एपीआई की मदद से, DOM में सुरक्षित बदलाव करना

नए सैनिटाइज़र एपीआई का मकसद, आर्बिट्रेरी स्ट्रिंग के लिए एक मज़बूत प्रोसेसर बनाना है, ताकि उन्हें पेज में सुरक्षित तरीके से डाला जा सके.

Jack J
Jack J

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

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

// Expanded Safely !!
$div.setHTML(`<em>hello world</em><img src="" onerror=alert(0)>`, new Sanitizer())

उपयोगकर्ता के इनपुट को एस्केप किया जा रहा है

डीओएम में उपयोगकर्ता का इनपुट, क्वेरी स्ट्रिंग, कुकी का कॉन्टेंट वगैरह डालते समय, स्ट्रिंग को सही तरीके से एस्केप किया जाना चाहिए. .innerHTML की मदद से DOM मैनिप्यूलेशन पर खास ध्यान दिया जाना चाहिए, जहां अनस्केप्ड स्ट्रिंग, XSS के सामान्य स्रोत होती हैं.

const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
$div.innerHTML = user_input

अगर ऊपर दी गई इनपुट स्ट्रिंग में एचटीएमएल के खास वर्णों को एस्केप या .textContent का इस्तेमाल करके बड़ा किया जाता है, तो alert(0) नहीं चलेगा. हालांकि, उपयोगकर्ता ने <em> को जोड़ा है, इसलिए उसे भी स्ट्रिंग के तौर पर बड़ा किया जाता है. इसलिए, टेक्स्ट की सजावट को एचटीएमएल में बनाए रखने के लिए, इस तरीके का इस्तेमाल नहीं किया जा सकता.

यहां से बचने के लिए सबसे अच्छा तरीका से बचना नहीं है, बल्कि सफ़ाई करना है.

उपयोगकर्ता का इनपुट साफ़ किया जा रहा है

एस्केपिंग और सैनिटाइज़िंग के बीच का अंतर

एस्केपिंग का मतलब है, खास एचटीएमएल वर्णों को एचटीएमएल इकाइयों से बदलना.

सैनिटाइज़िंग का मतलब है एचटीएमएल स्ट्रिंग से शब्दों के हिसाब से नुकसान पहुंचाने वाले हिस्सों को हटाना, जैसे कि स्क्रिप्ट चलाना.

उदाहरण

पिछले उदाहरण में, <img onerror> की वजह से गड़बड़ी हैंडलर लागू होता है. हालांकि, अगर onerror हैंडलर को हटाया गया है, तो <em> को बरकरार रखते हुए डीओएम में सुरक्षित तरीके से बड़ा किया जा सकता है.

// XSS 🧨
$div.innerHTML = `<em>hello world</em><img src="" onerror=alert(0)>`
// Sanitized ⛑
$div.innerHTML = `<em>hello world</em><img src="">`

सही तरीके से सैनिटाइज़ करने के लिए, यह ज़रूरी है कि इनपुट स्ट्रिंग को एचटीएमएल के रूप में पार्स किया जाए. साथ ही, नुकसान पहुंचाने वाले टैग और एट्रिब्यूट को मिटा दें. साथ ही, इनसे नुकसान न पहुंचाने वाले टैग और एट्रिब्यूट को भी हटा दें.

सुझाई गई सैनिटाइज़र एपीआई की खास जानकारी का मकसद, ब्राउज़र के लिए इस तरह की प्रोसेसिंग को स्टैंडर्ड एपीआई के तौर पर उपलब्ध कराना है.

सैनिटाइज़र एपीआई

Sanitizer API का इस्तेमाल इस तरह से किया जाता है:

const $div = document.querySelector('div')
const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
$div.setHTML(user_input, { sanitizer: new Sanitizer() }) // <div><em>hello world</em><img src=""></div>

हालांकि, { sanitizer: new Sanitizer() } डिफ़ॉल्ट आर्ग्युमेंट है. इसलिए, यह नीचे जैसा ही हो सकता है.

$div.setHTML(user_input) // <div><em>hello world</em><img src=""></div>

ध्यान दें कि setHTML() की जानकारी, Element पर दी गई है. Element का तरीका होने के नाते, पार्स करने का संदर्भ अपने-आप ही समझाता है (इस मामले में <div>), पार्स करने के बाद अंदरूनी तौर पर एक बार किया जाता है और नतीजा सीधे डीओएम में बढ़ाया जाता है.

सैनिटाइज़ेशन के नतीजे को स्ट्रिंग के तौर पर पाने के लिए, setHTML() नतीजों में से .innerHTML का इस्तेमाल किया जा सकता है.

const $div = document.createElement('div')
$div.setHTML(user_input)
$div.innerHTML // <em>hello world</em><img src="">

कॉन्फ़िगरेशन की मदद से पसंद के मुताबिक बनाएं

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

const config = {
  allowElements: [],
  blockElements: [],
  dropElements: [],
  allowAttributes: {},
  dropAttributes: {},
  allowCustomElements: true,
  allowComments: true
};
// sanitized result is customized by configuration
new Sanitizer(config)

इन विकल्पों से यह तय होता है कि सैनिटाइज़ेशन के नतीजे में, बताए गए एलिमेंट को किस तरह दिखाया जाना चाहिए.

allowElements: उन एलिमेंट के नाम जिन्हें सैनिटाइज़र में बनाए रखना चाहिए.

blockElements: उन एलिमेंट के नाम जिन्हें सैनिटाइज़र से हटा दिया जाना चाहिए, लेकिन इनसे बच्चों का डेटा सुरक्षित रहेगा.

dropElements: सैनिटाइज़र से हटाए जाने वाले एलिमेंट और बच्चों के नाम.

const str = `hello <b><i>world</i></b>`

$div.setHTML(str)
// <div>hello <b><i>world</i></b></div>

$div.setHTML(str, { sanitizer: new Sanitizer({allowElements: [ "b" ]}) })
// <div>hello <b>world</b></div>

$div.setHTML(str, { sanitizer: new Sanitizer({blockElements: [ "b" ]}) })
// <div>hello <i>world</i></div>

$div.setHTML(str, { sanitizer: new Sanitizer({allowElements: []}) })
// <div>hello world</div>

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

  • allowAttributes
  • dropAttributes

allowAttributes और dropAttributes प्रॉपर्टी के लिए, एट्रिब्यूट से मिलती-जुलती सूचियों की ज़रूरत होती है. ये ऐसे ऑब्जेक्ट होते हैं जिनकी कुंजियां, एट्रिब्यूट के नाम होती हैं और वैल्यू, टारगेट एलिमेंट या * वाइल्डकार्ड की सूचियां होती हैं.

const str = `<span id=foo class=bar style="color: red">hello</span>`

$div.setHTML(str)
// <div><span id="foo" class="bar" style="color: red">hello</span></div>

$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["span"]}}) })
// <div><span style="color: red">hello</span></div>

$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["p"]}}) })
// <div><span>hello</span></div>

$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["*"]}}) })
// <div><span style="color: red">hello</span></div>

$div.setHTML(str, { sanitizer: new Sanitizer({dropAttributes: {"id": ["span"]}}) })
// <div><span class="bar" style="color: red">hello</span></div>

$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {}}) })
// <div>hello</div>

allowCustomElements में कस्टम एलिमेंट को अनुमति देने या अस्वीकार करने का विकल्प होता है. अगर उनकी अनुमति है, तो एलिमेंट और एट्रिब्यूट के दूसरे कॉन्फ़िगरेशन अब भी लागू होते हैं.

const str = `<custom-elem>hello</custom-elem>`

$div.setHTML(str)
// <div></div>

const sanitizer = new Sanitizer({
  allowCustomElements: true,
  allowElements: ["div", "custom-elem"]
})
$div.setHTML(str, { sanitizer })
// <div><custom-elem>hello</custom-elem></div>

एपीआई का प्लैटफ़ॉर्म

DomPurify के साथ तुलना

DOMPurify एक मशहूर लाइब्रेरी है, जो साफ़-सफ़ाई की सुविधा देती है. Sanitizer API और DOMPurify के बीच मुख्य अंतर यह है कि DOMPurify, सैनिटाइज़ेशन के नतीजे को स्ट्रिंग के तौर पर दिखाता है. यह वैल्यू, .innerHTML के ज़रिए DOM एलिमेंट में लिखना होती है.

const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
const sanitized = DOMPurify.sanitize(user_input)
$div.innerHTML = sanitized
// `<em>hello world</em><img src="">`

ब्राउज़र में Sanitizer एपीआई लागू न होने पर, DOMPurify एक फ़ॉलबैक के तौर पर काम कर सकता है.

DOMPurify को लागू करने में कुछ समस्याएं आती हैं. अगर कोई स्ट्रिंग दी जाती है, तो इनपुट स्ट्रिंग को DOMPurify और .innerHTML से दो बार पार्स किया जाता है. डबल पार्स करने से, प्रोसेस करने में ज़्यादा समय लगता है. हालांकि, इससे जोखिम की आशंकाएं पैदा हो सकती हैं. ऐसा उन मामलों में होता है जिनमें दूसरी पार्सिंग का नतीजा पहले से अलग होता है.

एचटीएमएल को पार्स करने के लिए कॉन्टेक्स्ट भी ज़रूरी है. उदाहरण के लिए, <table> में <td> सही है, लेकिन <div> में नहीं. DOMPurify.sanitize(), स्ट्रिंग को सिर्फ़ तर्क के तौर पर लेता है. इसलिए, पार्स करने के कॉन्टेक्स्ट का अनुमान लगाना ज़रूरी था.

Sanitizer एपीआई, DOMPurify अप्रोच से बेहतर होती है. साथ ही, इसे डबल पार्स करने की ज़रूरत को खत्म करने और पार्स करने के कॉन्टेक्स्ट को साफ़ तौर पर बताने के लिए डिज़ाइन किया गया है.

एपीआई का स्टेटस और ब्राउज़र सहायता

सैनिटाइज़र एपीआई के स्टैंडर्ड तय करने की प्रक्रिया पर चर्चा की जा रही है. साथ ही, Chrome इसे लागू कर रहा है.

चरण स्थिति
1. जानकारी बनाएं पूरा हुआ
2. खास जानकारी का ड्राफ़्ट बनाएं पूरा हुआ
3. लोगों की राय जानें और डिज़ाइन को बेहतर बनाएं पूरा हुआ
4. Chrome का ऑरिजिन ट्रायल पूरा हुआ
5. लॉन्च करना M105 पर शिप करने का इरादा

Mozilla: इस प्रस्ताव को प्रोटोटाइप के लायक माना जाता है. साथ ही, हम इसे बेहतर तरीके से लागू कर रहे हैं.

WebKit: WebKit के ईमेल पाने वाले लोगों की सूची पर जवाब देखें.

Sanitizer एपीआई चालू करने का तरीका

ब्राउज़र सहायता

  • x
  • x
  • x

सोर्स

about://flags या सीएलआई से चालू करने का विकल्प

Chrome

Chrome, Sanitizer एपीआई लागू कर रहा है. Chrome 93 या उसके बाद के वर्शन में, about://flags/#enable-experimental-web-platform-features फ़्लैग को चालू करके इस तरीके को आज़माया जा सकता है. Chrome कैनरी और डेव चैनल के पुराने वर्शन में, इसे --enable-blink-features=SanitizerAPI से चालू किया जा सकता था और इसे अभी आज़माया जा सकता है. Chrome को फ़्लैग के साथ चलाने के निर्देश देखें.

Firefox

Firefox भी सैनिटाइज़र एपीआई को एक एक्सपेरिमेंटल फ़ीचर के तौर पर लागू करता है. इसे चालू करने के लिए, about:config में dom.security.sanitizer.enabled फ़्लैग को true पर सेट करें.

सुविधा की पहचान करने की सुविधा

if (window.Sanitizer) {
  // Sanitizer API is enabled
}

सुझाव/राय दें या शिकायत करें

अगर आप इस एपीआई का इस्तेमाल करते हैं और आपके पास कोई सुझाव है, तो हमें ज़रूर बताएं. Sanitizer API GitHub से जुड़ी समस्याओं पर अपनी राय शेयर करें. साथ ही, इस एपीआई में दिलचस्पी रखने वाले लोगों और लेखकों के साथ बातचीत करें.

अगर आपको Chrome के लागू करने में कोई गड़बड़ी या अनचाहा व्यवहार दिखता है, तो गड़बड़ी की शिकायत करने के लिए उसकी शिकायत करें. Blink>SecurityFeature>SanitizerAPI कॉम्पोनेंट चुनें और समस्या को लागू करने वाले लोगों की मदद करने के लिए जानकारी शेयर करें.

डेमो

Sanitizer एपीआई को इस्तेमाल करने का तरीका जानने के लिए, माइक वेस्ट के Sanitizer API प्लेग्राउंड पर जाएं:

रेफ़रंस


Unsplash पर तौफ़ीक बार्भूइया की फ़ोटो.