معالجة DOM الآمن باستخدام واجهة برمجة تطبيقات Sanitizer

تهدف واجهة Sanitizer API الجديدة إلى تصميم معالج قوي لإدخال السلاسل العشوائية في الصفحة بأمان.

Jack J
Jack J

تتعامل التطبيقات مع سلاسل غير موثوق بها طوال الوقت، ولكن قد يكون عرض هذا المحتوى بأمان كجزء من مستند HTML أمرًا صعبًا. بدون عناية كافية، من السهل إنشاء فرص للنصوص البرمجية على المواقع الإلكترونية (XSS) التي قد يستغلها المهاجمون الضارون.

للحدّ من هذا الخطر، يهدف اقتراح Sanitizer API الجديد إلى إنشاء معالج فعّال لإدراج السلاسل العشوائية بأمان في الصفحة. تقدم هذه المقالة مقدمة عن واجهة برمجة التطبيقات وتشرح طريقة استخدامها.

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

الخروج من البيانات التي أدخلها المستخدم

عند إدراج إدخال المستخدم وسلاسل طلبات البحث ومحتوى ملفات تعريف الارتباط وما إلى ذلك في نموذج العناصر في المستند (DOM)، يجب تخطي السلاسل بشكلٍ صحيح. يجب إيلاء اهتمام خاص لمعالجة DOM من خلال .innerHTML، حيث تكون السلاسل التي لا يتم تجاوزها مصدرًا نموذجيًا لـ XSS.

const user_input = `<em>hello world</em><img src="" onerror=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="" onerror=alert(0)>`
// Sanitized ⛑
$div.innerHTML = `<em>hello world</em><img src="">`

للتنقيح بشكل صحيح، من الضروري تحليل سلسلة الإدخال كHTML، وحذف العلامات والسمات التي تعتبر ضارة، والاحتفاظ بالأخرى غير الضارة.

تهدف مواصفات واجهة برمجة التطبيقات المقترحة لـ Sanitizer إلى توفير هذه المعالجة كواجهة برمجة تطبيقات عادية للمتصفّحات.

واجهة برمجة التطبيقات المطروحة

يتم استخدام 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.

للحصول على نتيجة التعقيم كسلسلة، يمكنك استخدام .innerHTML من نتائج setHTML().

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 يعرض نتيجة عملية التعقيم كسلسلة بيانات، والتي تحتاج إلى كتابتها في عنصر DOM من خلال .innerHTML.

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="">`

يمكن أن يكون DOMPurify بمثابة عنصر احتياطي في حال عدم تنفيذ Sanitizer API في المتصفّح.

هناك بعض الجوانب السلبية لتنفيذ DOMPurify. في حال عرض سلسلة، سيتم تحليل سلسلة الإدخال مرتين، من خلال DOMPurify و.innerHTML. يهدر هذا التحليل المزدوج وقت المعالجة، ولكنه يمكن أن يؤدي أيضًا إلى ثغرات أمنية مثيرة للاهتمام ناتجة عن الحالات التي تختلف فيها نتيجة التحليل الثاني عن النتيجة الأولى.

تحتاج HTML أيضًا إلى السياق لتحليلها. على سبيل المثال، تكون اللغة <td> منطقية في <table>، ولكن ليس في <div>. وبما أنّ السمة DOMPurify.sanitize() لا تأخذ السلسلة إلّا كوسيطة، يجب تخمين سياق التحليل.

تعمل Sanitizer API على تحسين نهج DOMPurify وتم تصميمه للحدّ من الحاجة إلى إجراء تحليل مزدوج وتوضيح سياق التحليل.

حالة واجهة برمجة التطبيقات وتوافقها مع المتصفّح

تتم مناقشة واجهة برمجة التطبيقات Sanitizer في عملية توحيد المقاييس ويعمل Chrome حاليًا على تنفيذها.

الخطوة الحالة
1- إنشاء شرح مكتمل
2. إنشاء مسودة للمواصفات مكتمل
3- جمع الملاحظات والتكرار التحسيني على التصميم مكتمل
4. مرحلة التجربة والتقييم في Chrome مكتمل
5- الإطلاق ينوون الشحن على M105

Mozilla: يرى هذا الاقتراح يستحق إنشاء نماذج أولية، وينفّذه بفعالية.

WebKit: يمكنك الاطلاع على الرد على القائمة البريدية لWebKit.

كيفية تفعيل واجهة برمجة التطبيقات Sanitizer

التوافق مع المتصفح

  • x
  • x
  • x

المصدر

التفعيل عبر about://flags أو خيار واجهة سطر الأوامر

Chrome

يجري حاليًا تنفيذ واجهة برمجة تطبيقات Sanitizer. في الإصدار 93 من Chrome أو الإصدارات الأحدث، يمكنك تجربة السلوك من خلال تفعيل العلامة about://flags/#enable-experimental-web-platform-features. في الإصدارات السابقة من Chrome Canary وقناة مطوّري البرامج، يمكنك تفعيلهما من خلال --enable-blink-features=SanitizerAPI وتجربتهما الآن. اطّلِع على التعليمات حول كيفية تشغيل Chrome باستخدام العلامات.

Firefox

يستخدم Firefox أيضًا واجهة برمجة تطبيقات Sanitizer كميزة تجريبية. لتفعيل هذه الميزة، عليك ضبط العلامة dom.security.sanitizer.enabled على true في about:config.

رصد الميزات

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

إضافة ملاحظات

إذا جرَّبت واجهة برمجة التطبيقات هذه وكان لديك بعض الملاحظات، يسعدنا تلقّيها. يمكنك مشاركة أفكارك حول مشاكل GitHub في واجهة برمجة تطبيقات Sanitizer ومناقشتها مع مؤلفي المواصفات والأشخاص المهتمين بواجهة برمجة التطبيقات هذه.

إذا وجدت أي أخطاء أو سلوك غير متوقع في تنفيذ Chrome، يُرجى الإبلاغ عن الخطأ للإبلاغ عنه. اختَر مكوّنات Blink>SecurityFeature>SanitizerAPI وشارِك التفاصيل لمساعدة المنفّذين على تتبُّع المشكلة.

الخصائص الديموغرافية

للاطّلاع على واجهة برمجة التطبيقات Sanitizer API أثناء تنفيذها، يمكنك مراجعة صفحة Sanitizer API Playground من إعداد Mike West:

المراجع


الصورة من تصوير توفيق باربوا على UnLaunch