تهدف واجهة برمجة التطبيقات الجديدة Sanitizer API إلى إنشاء معالج قوي للسلاسل العشوائية ليتم إدراجها بأمان في صفحة.
تتعامل التطبيقات مع سلاسل غير موثوق بها طوال الوقت، ولكن قد يكون عرض هذا المحتوى بأمان كجزء من مستند HTML أمرًا صعبًا. إذا لم يتم اتّخاذ الاحتياطات اللازمة، من السهل أن تنشأ عن طريق الخطأ فرص للهجمات عبر المواقع الإلكترونية (XSS) التي قد يستغلّها مهاجمون ضارون.
للحدّ من هذه المخاطر، يهدف اقتراح Sanitizer API الجديد إلى إنشاء معالج قوي للسلاسل العشوائية ليتم إدراجها بأمان في الصفحة. توضّح هذه المقالة واجهة برمجة التطبيقات وتشرح كيفية استخدامها.
// Expanded Safely !!
$div.setHTML(`<em>hello world</em><img src="" onerro>r=alert(0)`, new Sanitizer())
تجنُّب استخدام بيانات أدخلها المستخدم
عند إدراج مدخلات المستخدم وسلاسل طلبات البحث ومحتوى ملفات تعريف الارتباط وما إلى ذلك في DOM، يجب استخدام أحرف الإلغاء على نحو صحيح في السلاسل. يجب الانتباه بشكل خاص إلى تعديل DOM من خلال .innerHTML، حيث تكون السلاسل غير المرمّزة مصدرًا نموذجيًا لهجمات XSS.
const user_input = `<em>hello world</em><img src="" onerro>r=alert(0)`
$div.innerHTML = user_input
في حال إلغاء الأحرف الخاصة في HTML في سلسلة الإدخال أعلاه أو توسيعها باستخدام .textContent، لن يتم تنفيذ alert(0). ومع ذلك، بما أنّ <em> التي يضيفها المستخدم يتم توسيعها أيضًا كسلسلة كما هي، لا يمكن استخدام هذه الطريقة من أجل الاحتفاظ بتزيين النص في HTML.
أفضل إجراء يمكن اتّخاذه هنا ليس الهروب، بل التطهير.
تنظيف بيانات المستخدم
الفرق بين الهروب من السياق والتطهير
يشير مصطلح "الهروب" إلى استبدال رموز HTML الخاصة بوحدات HTML.
تشير عملية التنظيف إلى إزالة الأجزاء الضارة دلاليًا (مثل تنفيذ النصوص البرمجية) من سلاسل HTML.
مثال
في المثال السابق، يؤدي <img onerror> إلى تنفيذ معالج الأخطاء، ولكن إذا تمت إزالة المعالج onerror، سيكون من الممكن توسيعه بأمان في نموذج المستند (DOM) مع ترك <em> كما هو.
// 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=""`
لإجراء عملية التنظيف بشكل صحيح، من الضروري تحليل سلسلة الإدخال على أنّها HTML، وحذف العلامات والسمات التي تُعتبر ضارة، والاحتفاظ بالعلامات والسمات غير الضارة.
تهدف مواصفات 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.
للحصول على نتيجة التنظيف كسلسلة، يمكنك استخدام .innerHTML من نتائج setHTML().
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 تعرض نتيجة التنظيف كسلسلة، ما عليك سوى كتابتها في عنصر DOM من خلال .innerHTML.
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=""`
يمكن استخدام DOMPurify كحلّ احتياطي عندما لا يتم تنفيذ Sanitizer API في المتصفح.
يتضمّن تنفيذ DOMPurify بعض السلبيات. إذا تم عرض سلسلة، سيتم تحليل سلسلة الإدخال مرتين، بواسطة DOMPurify و.innerHTML. يؤدي هذا التحليل المزدوج إلى إهدار وقت المعالجة، ولكن يمكن أن يؤدي أيضًا إلى ثغرات أمنية مثيرة للاهتمام ناتجة عن حالات يختلف فيها نتيجة التحليل الثاني عن الأول.
يحتاج HTML أيضًا إلى سياق ليتم تحليله. على سبيل المثال، يكون <td> منطقيًا في <table>، ولكن ليس في <div>. بما أنّ الدالة DOMPurify.sanitize() لا تقبل سوى سلسلة كمعلَمة، كان يجب تخمين سياق التحليل.
تعمل Sanitizer API على تحسين طريقة DOMPurify، وهي مصمَّمة لإزالة الحاجة إلى التحليل المزدوج وتوضيح سياق التحليل.
حالة واجهة برمجة التطبيقات والمتصفّحات المتوافقة
تتم مناقشة Sanitizer API في عملية التوحيد، ويعمل Chrome على تنفيذها.
| الخطوة | الحالة |
|---|---|
| 1- إنشاء فيديو توضيحي | مكتمل |
| 2. إنشاء مسودّة مواصفات | مكتمل |
| 3- جمع الملاحظات وتكرار التصميم | مكتمل |
| 4. مرحلة التجربة والتقييم في Chrome | مكتمل |
| 5- إطلاق | إعلان عن نية الشحن في الإصدار 105 من Chrome |
Mozilla: ترى أنّ هذا الاقتراح يستحقّ إنشاء نموذج أولي، وتعمل على تنفيذه.
WebKit: يمكنك الاطّلاع على الردّ في قائمة WebKit البريدية.
كيفية تفعيل Sanitizer API
Browser Support
التفعيل من خلال about://flags أو خيار واجهة سطر الأوامر
Chrome
يعمل Chrome حاليًا على تنفيذ واجهة برمجة التطبيقات Sanitizer API. في الإصدار 93 من Chrome أو الإصدارات الأحدث، يمكنك تجربة السلوك من خلال تفعيل العلامة about://flags/#enable-experimental-web-platform-features. في الإصدارات السابقة من Chrome Canary وقناة الإصدارات التجريبية، يمكنك تفعيل هذه الميزة من خلال --enable-blink-features=SanitizerAPI وتجربتها الآن. اطّلِع على التعليمات حول كيفية تشغيل Chrome باستخدام الميزات التجريبية.
Firefox
يوفّر Firefox أيضًا واجهة Sanitizer API كميزة تجريبية. لتفعيلها، اضبط العلامة dom.security.sanitizer.enabled على true في about:config.
رصد الميزات
if (window.Sanitizer) {
// Sanitizer API is enabled
}
الملاحظات
إذا جرّبت واجهة برمجة التطبيقات هذه وكانت لديك بعض الملاحظات، يسعدنا تلقّيها. يمكنك مشاركة أفكارك حول مشاكل Sanitizer API على GitHub ومناقشتها مع مؤلفي المواصفات والأشخاص المهتمين بواجهة برمجة التطبيقات هذه.
إذا رصدت أي أخطاء أو سلوكًا غير متوقّع في تنفيذ Chrome، يُرجى إرسال تقرير عن الخطأ للإبلاغ عنه. اختَر مكوّنات Blink>SecurityFeature>SanitizerAPI وشارك التفاصيل لمساعدة المنفّذين في تتبُّع المشكلة.
عرض توضيحي
للاطّلاع على طريقة عمل Sanitizer API، يمكنك الانتقال إلى Sanitizer API Playground الذي أنشأه Mike West:
المراجع
- مواصفات واجهة برمجة التطبيقات HTML Sanitizer
- مستودع WICG/sanitizer-api
- الأسئلة الشائعة حول Sanitizer API
- المستندات المرجعية لواجهة برمجة التطبيقات HTML Sanitizer على شبكة مطوّري Mozilla
الصورة من إعداد Towfiqu barbhuiya على Unsplash.