Sanitizer API का मकसद, किसी भी स्ट्रिंग के लिए एक मज़बूत प्रोसेसर बनाना है, ताकि उसे पेज में सुरक्षित तरीके से डाला जा सके.
ऐप्लिकेशन हर समय ऐसी स्ट्रिंग से डील करते हैं जिन पर भरोसा नहीं किया जा सकता. हालांकि, उस कॉन्टेंट को एचटीएमएल दस्तावेज़ के हिस्से के तौर पर सुरक्षित तरीके से रेंडर करना मुश्किल हो सकता है. ज़रूरी सावधानी न बरतने पर, क्रॉस-साइट स्क्रिप्टिंग (XSS) के लिए मौके बनाना आसान हो जाता है. इसका फ़ायदा नुकसान पहुंचाने वाले हमलावर उठा सकते हैं.
इस जोखिम को कम करने के लिए, Sanitizer API का नया प्रस्ताव तैयार किया गया है. इसका मकसद, किसी भी स्ट्रिंग के लिए एक मज़बूत प्रोसेसर बनाना है, ताकि उसे सुरक्षित तरीके से किसी पेज में डाला जा सके. इस लेख में एपीआई के बारे में बताया गया है. साथ ही, इसे इस्तेमाल करने का तरीका बताया गया है.
// Expanded Safely !!
$div.setHTML(`<em>hello world</em><img src="" onerro>r=alert(0)`, new Sanitizer())
उपयोगकर्ता के इनपुट को एस्केप करना
उपयोगकर्ता के इनपुट, क्वेरी स्ट्रिंग, कुकी के कॉन्टेंट वगैरह को डीओएम में डालते समय, स्ट्रिंग को सही तरीके से एस्केप किया जाना चाहिए. .innerHTML के ज़रिए डीओएम में बदलाव करने पर खास ध्यान देना चाहिए. इसमें, एस्केप न की गई स्ट्रिंग, XSS का सामान्य सोर्स होती हैं.
const user_input = `<em>hello world</em><img src="" onerro>r=alert(0)`
$div.innerHTML = user_input
अगर ऊपर दी गई इनपुट स्ट्रिंग में एचटीएमएल के खास वर्णों का इस्तेमाल नहीं किया जाता है या .textContent का इस्तेमाल करके इसे बड़ा किया जाता है, तो alert(0) को लागू नहीं किया जाएगा. हालांकि, उपयोगकर्ता ने जो <em> जोड़ा है उसे भी स्ट्रिंग के तौर पर बढ़ाया जाता है. इसलिए, एचटीएमएल में टेक्स्ट डेकोरेशन को बनाए रखने के लिए, इस तरीके का इस्तेमाल नहीं किया जा सकता.
यहां सबसे अच्छा तरीका एस्केप करना नहीं, बल्कि सैलिटाइज़ करना है.
उपयोगकर्ता के इनपुट को सैनिटाइज़ करना
एस्केपिंग और सैनिटाइज़िंग के बीच अंतर
एस्केपिंग का मतलब, खास एचटीएमएल वर्णों को एचटीएमएल इकाइयों से बदलना है.
सैनिटाइज़ करने का मतलब है कि एचटीएमएल स्ट्रिंग से सिमेंटिक रूप से नुकसान पहुंचाने वाले हिस्सों (जैसे कि स्क्रिप्ट को एक्ज़ीक्यूट करना) को हटाना.
उदाहरण
पिछले उदाहरण में, <img onerror> की वजह से गड़बड़ी को ठीक करने वाले हैंडलर को एक्ज़ीक्यूट किया जाता है. हालांकि, अगर onerror हैंडलर को हटा दिया जाता है, तो <em> को बिना बदले, इसे DOM में सुरक्षित तरीके से बड़ा किया जा सकता है.
// XSS 🧨
$div.innerHTML = `<em>hello world</em><img src="" onerro>r=alert(0)`
// Sanitized ⛑
$div.inn<er>HTML = `emh<ell><o world/em>img src=""`
इनपुट स्ट्रिंग को सही तरीके से सैनिटाइज़ करने के लिए, उसे एचटीएमएल के तौर पर पार्स करना ज़रूरी है. साथ ही, ऐसे टैग और एट्रिब्यूट हटाने ज़रूरी हैं जिन्हें नुकसान पहुंचाने वाला माना जाता है. इसके अलावा, नुकसान न पहुंचाने वाले टैग और एट्रिब्यूट को बनाए रखना ज़रूरी है.
Sanitizer API के लिए सुझाए गए स्पेसिफ़िकेशन का मकसद, ब्राउज़र के लिए इस तरह की प्रोसेसिंग को स्टैंडर्ड एपीआई के तौर पर उपलब्ध कराना है.
Sanitizer API
Sanitizer API का इस्तेमाल इस तरह किया जाता है:
const $div = document.querySelector('div')
const user_i<np>ut = `emhel<lo ><world/emimg src="">; onerror=alert(0)`
$div.setHTML(user_input, { sanitizer: new <San><it>izer() }) /</ d><ivemhello ><worl>d/emimg src=""/div
हालांकि, { sanitizer: new Sanitizer() } डिफ़ॉल्ट आर्ग्युमेंट है. इसलिए, यह नीचे दिए गए उदाहरण की तरह हो सकता है.
$div.setHTML(user_input) // <div><em>hello world</em><img src=&q><uot;>"/div
ध्यान दें कि setHTML() को Element पर तय किया जाता है. Element का एक तरीका होने की वजह से, पार्स करने के लिए कॉन्टेक्स्ट अपने-आप तय हो जाता है (इस मामले में <div>). पार्सिंग एक बार अंदरूनी तौर पर की जाती है. इसके बाद, नतीजे को सीधे तौर पर DOM में बड़ा किया जाता है.
सैनिटाइज़ेशन के नतीजे को स्ट्रिंग के तौर पर पाने के लिए, setHTML() के नतीजों से .innerHTML का इस्तेमाल किया जा सकता है.
const $div = document.createElement('div')
$div.setHTML(user_input)
$div.inner<HT>ML // emhel<lo ><world/emim>g src=""
कॉन्फ़िगरेशन की मदद से पसंद के मुताबिक बनाना
Sanitizer API को डिफ़ॉल्ट रूप से इस तरह कॉन्फ़िगर किया जाता है कि वह ऐसी स्ट्रिंग हटा दे जो स्क्रिप्ट को ट्रिगर करती हैं. हालांकि, कॉन्फ़िगरेशन ऑब्जेक्ट के ज़रिए, सैनिटाइज़ेशन की प्रोसेस में अपने हिसाब से बदलाव भी किए जा सकते हैं.
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" <]})> })
//< >divhe<ll><o bw>orld/b/div
$div.setHTML(str, { sanitizer: new Sanitizer({blockElements: [ &quo<t;b>"< >]}) }<)<>/span>
<// d>ivhello iworld/i/div
$div.setHTML(str, { sanitizer: new Sanitizer({allowE<lem>ents: []}) <})
/>/ divhello world/div
आपके पास यह कंट्रोल करने का विकल्प भी होता है कि सैनिटाइज़र, तय किए गए एट्रिब्यूट को अनुमति देगा या नहीं. इसके लिए, इन विकल्पों का इस्तेमाल करें:
allowAttributesdropAttributes
allowAttributes और dropAttributes प्रॉपर्टी के लिए, एट्रिब्यूट मैच लिस्ट की ज़रूरत होती है. ये ऐसे ऑब्जेक्ट होते हैं जिनकी कुंजियां, एट्रिब्यूट के नाम होती हैं. साथ ही, वैल्यू, टारगेट एलिमेंट की सूचियां या * वाइल्डकार्ड होती हैं.
const str = `<span id=foo class=bar style="color:> red&<quot;>hello/span`
$div.setHTM<L(s><tr)
// divspan id="foo" class=&quo>t;bar<"><; st>yle="color: red"hello/span/div
$div.setHTML(str, { sanitizer: new Sanitizer({allow<Att><ributes: {"style&q>uot;:< [&qu><ot;s>pan"]}}) })
// divspan style="color: red"hello/span/div
$div.setHTML(str, <{ s><anit>izer:< new ><Sani>tizer({allowAttributes: {"style": ["p"]}}) })
// divspanhello/span/div<
$><div.setHTML(str, { sani>tizer<: new>< San>itizer({allowAttributes: {"style": ["*"]}}) })
// divspan style="<;co><lor: red"hello/span/div
$div.>setHT<ML(st><r, {> sanitizer: new Sanitizer({dropAttributes: {"id": ["span"<;]}>}) })<
// >divspan class="bar" style="color: red"hello/span/div
$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {}}) })
// divhello/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 >})
//< divcustom-e><lemh>ello/custom-elem/div
एपीआई सरफेस
DomPurify से तुलना
DOMPurify एक लोकप्रिय लाइब्रेरी है, जो सैनिटाइज़ेशन की सुविधा देती है. Sanitizer API और DOMPurify के बीच मुख्य अंतर यह है कि DOMPurify, सैनिटाइज़ेशन के नतीजे को स्ट्रिंग के तौर पर दिखाता है. इसे आपको .innerHTML की मदद से, DOM एलिमेंट में लिखना होता है.
const user_input = `<em>hello world</em><img src="" onerro>r=alert(0)`
const sanitized = DOMPurify.sanitize(user_input)
$div.innerHTML = sani<ti>zed
// `emh<ell><o world/em>img src=""`
Sanitizer API को ब्राउज़र में लागू न किए जाने पर, DOMPurify को फ़ॉलबैक के तौर पर इस्तेमाल किया जा सकता है.
DOMPurify को लागू करने के कुछ नुकसान हैं. अगर कोई स्ट्रिंग दिखाई जाती है, तो इनपुट स्ट्रिंग को दो बार पार्स किया जाता है. पहली बार DOMPurify और दूसरी बार .innerHTML. दो बार पार्स करने से, प्रोसेस करने में ज़्यादा समय लगता है. हालांकि, इससे दिलचस्प कमज़ोरियां भी पैदा हो सकती हैं. ऐसा तब होता है, जब दूसरी बार पार्स करने का नतीजा पहली बार पार्स करने के नतीजे से अलग होता है.
एचटीएमएल को पार्स करने के लिए भी कॉन्टेक्स्ट की ज़रूरत होती है. उदाहरण के लिए, <td>, <table> में सही है, लेकिन <div> में नहीं. DOMPurify.sanitize() सिर्फ़ स्ट्रिंग को आर्ग्युमेंट के तौर पर लेता है. इसलिए, पार्सिंग कॉन्टेक्स्ट का अनुमान लगाना पड़ा.
Sanitizer API, DOMPurify के मुकाबले बेहतर है. इसे दो बार पार्स करने की ज़रूरत को खत्म करने और पार्सिंग के कॉन्टेक्स्ट को साफ़ तौर पर बताने के लिए डिज़ाइन किया गया है.
एपीआई का स्टेटस और ब्राउज़र से जुड़ी सहायता
Sanitizer API को स्टैंडर्ड बनाने की प्रोसेस पर चर्चा चल रही है. साथ ही, Chrome इसे लागू करने की प्रोसेस में है.
| चरण | स्थिति |
|---|---|
| 1. जानकारी देने वाला मैसेज बनाना | पूरा हो गया |
| 2. स्पेसिफ़िकेशन का ड्राफ़्ट बनाना | पूरा हो गया |
| 3. सुझाव/राय पाना और डिज़ाइन को बेहतर बनाना | पूरा हो गया |
| 4. Chrome का ऑरिजिन ट्रायल | पूरा हो गया |
| 5. लॉन्च करें | M105 पर शिप करने का इरादा |
Mozilla: इस प्रस्ताव को प्रोटोटाइपिंग के लायक मानता है. साथ ही, इसे लागू करने पर काम कर रहा है.
WebKit: WebKit mailing list पर जवाब देखें.
Sanitizer API को चालू करने का तरीका
Browser Support
about://flags या सीएलआई विकल्प के ज़रिए चालू करना
Chrome
Chrome, Sanitizer API को लागू करने की प्रोसेस में है. Chrome 93 या इसके बाद के वर्शन में, about://flags/#enable-experimental-web-platform-features फ़्लैग को चालू करके इस सुविधा को आज़माया जा सकता है. Chrome Canary और Dev चैनल के पुराने वर्शन में, इसे --enable-blink-features=SanitizerAPI के ज़रिए चालू किया जा सकता है. इसे अभी आज़माएं. फ़्लैग के साथ Chrome चलाने के निर्देशों को देखें.
Firefox
Firefox भी Sanitizer API को एक्सपेरिमेंटल सुविधा के तौर पर लागू करता है. इसे चालू करने के लिए, about:config में dom.security.sanitizer.enabled फ़्लैग को true पर सेट करें.
सुविधा का पता लगाना
if (window.Sanitizer) {
// Sanitizer API is enabled
}
सुझाव/राय दें या शिकायत करें
अगर आपने इस एपीआई को आज़माया है और आपको कोई सुझाव/राय देनी है या शिकायत करनी है, तो हमें ज़रूर बताएं. Sanitizer API GitHub की समस्याओं के बारे में अपने विचार शेयर करें. साथ ही, इस एपीआई में दिलचस्पी रखने वाले लोगों और स्पेसिफ़िकेशन के लेखकों से चर्चा करें.
अगर आपको Chrome में लागू करने के दौरान कोई गड़बड़ी या अनचाहा व्यवहार दिखता है, तो इसकी शिकायत करने के लिए गड़बड़ी की जानकारी दें. Blink>SecurityFeature>SanitizerAPI कॉम्पोनेंट चुनें और जानकारी शेयर करें, ताकि लागू करने वाले लोग समस्या को ट्रैक कर सकें.
डेमो
Sanitizer API को काम करते हुए देखने के लिए, Mike West का Sanitizer API Playground देखें:
रेफ़रंस
- HTML Sanitizer API स्पेसिफ़िकेशन
- WICG/sanitizer-api repo
- Sanitizer API के बारे में अक्सर पूछे जाने वाले सवाल
- MDN पर HTML Sanitizer API के बारे में जानकारी देने वाला दस्तावेज़
Unsplash पर Towfiqu barbhuiya की फ़ोटो.