Sanitizer API के नए वर्शन का मकसद, किसी भी स्ट्रिंग को पेज में सुरक्षित तरीके से डालने के लिए, एक बेहतर प्रोसेसर बनाना है.
ऐप्लिकेशन हमेशा अविश्वसनीय स्ट्रिंग का इस्तेमाल करते हैं. हालांकि, एचटीएमएल दस्तावेज़ के हिस्से के तौर पर उस कॉन्टेंट को सुरक्षित तरीके से रेंडर करना मुश्किल हो सकता है. ज़रूरत के मुताबिक सावधानी न बरतने पर, अनजाने में क्रॉस-साइट स्क्रिप्टिंग (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 के लिए सुझाई गई खास जानकारी का मकसद, ब्राउज़र के लिए स्टैंडर्ड एपीआई के तौर पर ऐसी प्रोसेसिंग उपलब्ध कराना है.
Sanitizer API
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>
). पार्सिंग को अंदरूनी तौर पर एक बार किया जाता है और नतीजे को सीधे तौर पर DOM में बड़ा किया जाता है.
डेटा को सुरक्षित करने के बाद, उसे स्ट्रिंग के तौर पर पाने के लिए, setHTML()
के नतीजों में से .innerHTML
का इस्तेमाल किया जा सकता है.
const $div = document.createElement('div')
$div.setHTML(user_input)
$div.innerHTML // <em>hello world</em><img 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" ]}) })
// <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 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 की मेलिंग सूची पर जवाब देखें.
Sanitizer API को चालू करने का तरीका
about://flags
या सीएलआई विकल्प की मदद से चालू करना
Chrome
Chrome, Sanitizer API को लागू करने की प्रोसेस में है. Chrome 93 या उसके बाद के वर्शन में, about://flags/#enable-experimental-web-platform-features
फ़्लैग को चालू करके, इस सुविधा को आज़माया जा सकता है. Chrome Canary और डेवलपर चैनल के पुराने वर्शन में, --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 को काम करते हुए देखने के लिए, माइक वेस्ट का Sanitizer API Playground देखें:
रेफ़रंस
- HTML Sanitizer API स्पेसिफ़िकेशन
- WICG/sanitizer-api repo
- Sanitizer API के बारे में अक्सर पूछे जाने वाले सवाल
- MDN पर, एचटीएमएल सैनिटाइज़र एपीआई का रेफ़रंस दस्तावेज़
Unsplash पर Towfiqu barbhuiya की फ़ोटो.