تحدث البرمجة النصية على المواقع الإلكترونية المستندة إلى نموذج العناصر في المستند (DOM XSS) (DOM XSS) عندما تصل بيانات من مصدر يتحكّم فيه المستخدم (مثل اسم المستخدم أو عنوان URL لإعادة التوجيه مأخوذ من جزء عنوان URL) إلى حوض، وهو وظيفة مثل eval()
أو أداة ضبط خصائص مثل .innerHTML
يمكنها تنفيذ رمز JavaScript عشوائي.
يُعد DOM XSS واحدة من أكثر الثغرات الأمنية شيوعًا على الويب، ومن الشائع أن تدرجه فِرق مطوّري البرامج عن طريق الخطأ في تطبيقاتهم. وتمنحك الأنواع الموثوق بها أدوات لكتابة التطبيقات ومراجعة الأمان والحفاظ عليها خالية من ثغرات DOM XSS من خلال جعل وظائف واجهة برمجة تطبيقات الويب الخطيرة آمنة تلقائيًا. تتوفر الأنواع الموثوق بها باعتبارها polyfill للمتصفّحات التي لا تتوافق معها بعد.
الخلفية
كان DOM XSS لسنوات عديدة أحد أكثر الثغرات الأمنية انتشارًا وخطيرًا على الويب.
هناك نوعان من البرمجة النصية عبر المواقع. تنتج بعض ثغرات XSS عن رمز من جانب الخادم ينشئ بشكلٍ غير آمن رمز HTML الذي يشكل الموقع الإلكتروني. والبعض الآخر يكون له سبب أساسي في العميل، حيث يستدعي رمز JavaScript وظائف خطيرة بمحتوى يتحكّم فيه المستخدم.
لمنع استخدام XSS من جهة الخادم، لا تنشئ HTML من خلال تسلسل السلاسل. بدلاً من ذلك، يمكنك استخدام مكتبات نماذج افتراضية آمنة بدلاً من ذلك، بالإضافة إلى سياسة أمان محتوى غير مستندة إلى السياق للحدّ من الأخطاء الإضافية.
بإمكان المتصفّحات الآن أيضًا المساعدة في منع XSS المستنِد إلى نموذج العناصر في المستند (DOM) من جهة العميل باستخدام الأنواع الموثوق بها.
مقدمة عن واجهة برمجة التطبيقات
تعمل "الأنواع الموثوق بها" من خلال قفل وظائف الأحواض الخطرة التالية. ربما تتعرف بالفعل على بعضها، لأن مورّدي المتصفحات وأُطر عمل الويب يحولونك بالفعل عن استخدام هذه الميزات لأسباب أمنية.
- معالجة النصوص البرمجية:
<script src>
وضبط المحتوى النصي لعناصر<script>
. - إنشاء HTML من سلسلة:
- تنفيذ محتوى المكوّن الإضافي:
- تجميع رمز JavaScript لوقت التشغيل:
eval
setTimeout
setInterval
new Function()
تتطلب الأنواع الموثوق بها منك معالجة البيانات قبل تمريرها إلى دوال الأحواض هذه. يتعذّر استخدام سلسلة فقط، لأن المتصفح لا يعرف ما إذا كانت البيانات موثوقة أم لا:
anElement.innerHTML = location.href;
للإشارة إلى أنّه تمت معالجة البيانات بشكل آمن، يمكنك إنشاء كائن خاص - نوع موثوق به.
anElement.innerHTML = aTrustedHTML;
تحدّ الأنواع الموثوق بها من مساحة الهجوم في DOM XSS في تطبيقك بشكل كبير. يبسط هذا التطبيق مراجعات الأمان، ويتيح لك فرض فحوصات الأمان المستندة إلى النوع والتي يتم إجراؤها عند تجميع الرمز البرمجي أو دمجه أو دمجه في وقت التشغيل في المتصفّح.
كيفية استخدام الأنواع الموثوق بها
الاستعداد للإبلاغ عن انتهاك سياسة أمان المحتوى
يمكنك نشر أداة تجميع تقارير، مثل أداة go-csp-collector المفتوحة المصدر، أو استخدام أحد الأدوات التجارية المكافئة. يمكنك أيضًا تصحيح الأخطاء المرتبطة بالانتهاكات في المتصفّح:
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
إضافة عنوان CSP مخصّص لإعداد التقارير فقط
أضِف عنوان استجابة HTTP التالي إلى المستندات التي تريد نقلها إلى الأنواع الموثوق بها:
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
تم الآن إبلاغ //my-csp-endpoint.example
بجميع المخالفات، لكن الموقع الإلكتروني يواصل العمل. يوضّح القسم التالي آلية عمل
//my-csp-endpoint.example
.
تحديد انتهاكات سياسة "الأنواع الموثوق بها"
من الآن فصاعدًا، في كل مرة ترصد فيها الأنواع الموثوق بها انتهاكًا، يرسل المتصفّح
تقريرًا إلى report-uri
الذي تم إعداده. على سبيل المثال، عندما يمرّر تطبيقك سلسلة إلى innerHTML
، يرسل المتصفّح التقرير التالي:
{
"csp-report": {
"document-uri": "https://my.url.example",
"violated-directive": "require-trusted-types-for",
"disposition": "report",
"blocked-uri": "trusted-types-sink",
"line-number": 39,
"column-number": 12,
"source-file": "https://my.url.example/script.js",
"status-code": 0,
"script-sample": "Element innerHTML <img src=x"
}
}
يشير هذا إلى أنّه في https://my.url.example/script.js
في السطر 39، تم استدعاء innerHTML
بالسلسلة التي تبدأ بـ <img src=x
. من المفترض أن تساعدك هذه المعلومات في تضييق نطاق
أجزاء التعليمات البرمجية التي قد تقدم DOM XSS وتحتاج إلى التغيير.
إصلاح الانتهاكات
هناك خياران لإصلاح انتهاك "النوع الموثوق به". يمكنك إزالة الرمز المسيء أو استخدام مكتبة أو إنشاء سياسة "نوع موثوق به" أو إنشاء سياسة تلقائية كحل أخير.
إعادة كتابة الرمز البرمجي المسيء
من المحتمل أنّ الرمز غير المطابق لم يعُد مطلوبًا، أو يمكن إعادة كتابته بدون الدوال التي تؤدي إلى حدوث الانتهاكات:
el.textContent = ''; const img = document.createElement('img'); img.src = 'xyz.jpg'; el.appendChild(img);
el.innerHTML = '';
استخدام مكتبة
تنشئ بعض المكتبات بالفعل أنواعًا موثوقًا بها يمكنك تمريرها إلى دوال الأحواض. على سبيل المثال، يمكنك استخدام DOMPurify لتنظيف مقتطف HTML وإزالة حمولات XSS.
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
يتوافق DOMPurify مع الأنواع الموثوق بها
ويعرض رمز HTML معقّم ملفوفًا في كائن TrustedHTML
حتى لا ينشئ المتصفّح مخالفة.
إنشاء سياسة "نوع موثوق به"
ولا يمكنك أحيانًا إزالة الرمز الذي يتسبب في حدوث الانتهاك، ولا تتوفّر مكتبة لتنقيح القيمة وإنشاء نوع موثوق به لك. وفي هذه الحالات، يمكنك إنشاء كائن من النوع "موثوق" بنفسك.
أولاً، أنشئ سياسة. السياسات هي مصانع للأنواع الموثوق بها التي تفرض قواعد أمان معيّنة على البيانات التي تُدخلها:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
createHTML: string => string.replace(/\</g, '<')
});
}
ينشئ هذا الرمز سياسة تُسمى myEscapePolicy
يمكنها إنتاج كائنات TrustedHTML
باستخدام دالة createHTML()
الخاصة بها. وتمنع القواعد المحددة استخدام أحرف HTML من أحرف <
لمنع إنشاء عناصر HTML جديدة.
يمكنك استخدام السياسة على النحو التالي:
const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x onerror=alert(1)>'
استخدام سياسة تلقائية
لا يمكنك أحيانًا تغيير الرمز المخالف، على سبيل المثال، إذا كنت تحمِّل مكتبة تابعة لجهة خارجية من شبكة توصيل محتوى (CDN). في هذه الحالة، استخدِم سياسة تلقائية:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
trustedTypes.createPolicy('default', {
createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
});
}
يتم استخدام السياسة المُسمّاة default
في أي مكان يتم فيه استخدام سلسلة في مستودع لا يقبل سوى
"النوع الموثوق به".
التبديل إلى فرض "سياسة أمان المحتوى"
عندما يتوقف تطبيقك عن تقديم أي انتهاكات، يمكنك البدء في فرض الأنواع الموثوق بها:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
بغضّ النظر عن مدى تعقيد تطبيق الويب، لا يمكن إدخال ثغرة أمنية في DOM XSS إلا في الرموز البرمجية في إحدى السياسات، ويمكنك قفلها بشكل أكبر من خلال الحدّ من إمكانية إنشاء السياسات.