النصوص البرمجية للمواقع الإلكترونية (XSS)، وهي إمكانية حقن نصوص برمجية ضارة في تطبيق ويب، كانت لعدة عقود واحدة من أكبر الثغرات الأمنية في أمان الويب.
سياسة أمان المحتوى (CSP)
هي طبقة أمان إضافية تساعد في الحدّ من هجمات XSS. لإعداد سياسة خدمة حماية المحتوى،
أضِف عنوان HTTP Content-Security-Policy
إلى صفحة ويب واضبط القيم التي
تتحكم في الموارد التي يمكن لوكيل المستخدم تحميلها لهذه الصفحة.
توضّح هذه الصفحة كيفية استخدام سياسة أمان المحتوى المستندة إلى الرموز العشوائية أو التجزئات للحدّ من الهجمات التي تستخدم النصوص البرمجية على المواقع الإلكترونية (XSS)، بدلاً من سياسات أمان المحتوى المستندة إلى القائمة المسموح بها للمضيفين والتي غالبًا ما تترك الصفحة معرّضة للهجوم لأنّه يمكن تجاوزها في معظم عمليات الضبط.
المصطلح الرئيسي: المفتاح المؤقت هو رقم عشوائي يُستخدَم مرة واحدة فقط، ويمكنك استخدامه لتمييز علامة
<script>
على أنّها موثوق بها.
المصطلح الرئيسي: دالة التجزئة هي دالة رياضية تحوّل قيمة إدخال
إلى قيمة رقمية مضغوطة تُسمى تجزئة. يمكنك استخدام تجزئة
(مثل SHA-256) لوضع علامة على علامة
<script>
مضمّنة على أنّها موثوق بها.
يُطلق على سياسة أمان المحتوى المستندة إلى قيم عشوائية أو تجزئات اسم سياسة أمان المحتوى الصارمة. عندما يستخدم التطبيق سياسة CSP صارمة، لا يمكن للمهاجمين الذين يعثرون على عيوب في حقن HTML عمومًا استخدامها لإجبار المتصفّح على تنفيذ النصوص البرمجية الضارّة في مستند معرّض للاختراق. ويعود ذلك إلى أنّ سياسة CSP الصارمة تسمح فقط باستخدام النصوص البرمجية المشفَّرة أو النصوص البرمجية التي تحتوي على قيمة المفتاح العشوائي الصحيحة التي يتم إنشاؤها على الخادم، وبالتالي لا يمكن للمهاجمين تنفيذ النص البرمجي بدون معرفة المفتاح العشوائي الصحيح لاستجابة معيّنة.
لماذا عليك استخدام سياسة CSP صارمة؟
إذا كان موقعك الإلكتروني يتضمّن حاليًا خدمة CSP تشبه script-src www.googleapis.com
،
من المحتمل أنّها غير فعّالة في مواجهة الهجمات على مستوى المواقع الإلكترونية. يُعرف هذا النوع من مقدّمي خدمة المحتوى (CSP) باسم
مقدّم خدمة قائمة المسموح به. وتتطلّب هذه الحلول الكثير من التخصيص ويمكن للمهاجمين
تجاوزها.
وتتجنّب خدمات CSP الصارمة هذه المزالق استنادًا إلى أرقام التسلسل أو التجزئات المشفّرة.
بنية سياسة أمان المحتوى (CSP) الصارمة
تستخدِم سياسة أمان المحتوى الأساسية الصارمة أحد عناوين ملفّ تعريف الارتباط التالية لطلبات HTTP:
سياسة أمان المحتوى الصارمة المستندة إلى مفتاح عشوائي
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
سياسة أمان المحتوى الصارمة المستندة إلى التجزئة
Content-Security-Policy:
script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
تجعل السمات التالية خدمة إدارة مفاتيح المحتوى (CSP) مثل هذه "صارمة" وبالتالي آمنة:
- ويستخدم هذا الإجراء أرقامًا عشوائية
'nonce-{RANDOM}'
أو رموزًا هاشتاغ'sha256-{HASHED_INLINE_SCRIPT}'
للإشارة إلى علامات<script>
التي يثق بها مطوّر الموقع الإلكتروني لتنفيذها في متصفّح المستخدم. - ويحدّد
'strict-dynamic'
لخفض الجهد المبذول في نشر سياسة CSP المستندة إلى مفتاح عشوائي أو تجزئة من خلال السماح تلقائيًا بتنفيذ النصوص البرمجية التي ينشئها نص برمجي موثوق. ويؤدي ذلك أيضًا إلى إزالة حظر استخدام معظم مكتبات JavaScript وتطبيقات المصغّرات التابعة لجهات خارجية. - ولا يستند إلى قوائم المسموح بها لعناوين URL، لذا لا يواجه عمليات التحايل الشائعة على خدمة CSP.
- ويحظر النصوص البرمجية المضمّنة غير الموثوق بها، مثل معالجات الأحداث المضمّنة أو
javascript:
معرّفات الموارد المنتظمة (URI). - يفرض هذا الإعداد قيودًا على
object-src
لإيقاف الإضافات الخطيرة، مثل Flash. - يفرض هذا الإجراء قيودًا على
base-uri
لحظر حقن علامات<base>
. ويمنع ذلك المهاجمين من تغيير مواقع النصوص البرمجية المحمَّلة من عناوين URL نسبية.
اعتماد سياسة CSP صارمة
لاعتماد سياسة CSP صارمة، عليك إجراء ما يلي:
- حدِّد ما إذا كان يجب على تطبيقك ضبط سياسة CSP مستندة إلى مفتاح عشوائي أو تجزئة.
- انسخ سياسة CSP من قسم بنية سياسة CSP الصارمة واضبطها كعنوان استجابة في تطبيقك.
- إعادة صياغة نماذج HTML والرمز البرمجي من جهة العميل لإزالة الأنماط التي لا تتوافق مع CSP
- فعِّل سياسة أمان المحتوى (CSP).
يمكنك استخدام أفضل الممارسات في Lighthouse
(الإصدار 7.3.0 والإصدارات الأحدث التي تتضمّن العلامة --preset=experimental
)
أثناء هذه العملية للتحقّق مما إذا كان موقعك الإلكتروني يستخدم بروتوكول CSP، وما إذا كان
صارمًا بما يكفي ليكون فعّالاً في مكافحة هجمات XSS.
الخطوة 1: تحديد ما إذا كنت بحاجة إلى سياسة CSP مستندة إلى مفتاح عشوائي أو تجزئة
في ما يلي آلية عمل النوعَين من سياسة CSP الصارمة:
سياسة أمان المحتوى المستندة إلى الرمز المميّز لمرة واحدة
باستخدام سياسة CSP المستندة إلى مفتاح تشفير عشوائي، يمكنك إنشاء رقم عشوائي أثناء التشغيل وتضمينه في سياسة CSP وربطه بكل علامة نص برمجي في صفحتك. لا يمكن للمخترِق تضمين نص برمجي ضارّ أو تشغيله في صفحتك، لأنّه يحتاج إلى تخمين الرقم العشوائي الصحيح لهذا النص البرمجي. لا يعمل هذا الإجراء إلا إذا كان الرقم غير قابل للتخمين، ويتم إنشاؤه حديثًا أثناء التشغيل لكل إجابة.
استخدِم سياسة CSP المستندة إلى مفتاح عشوائي لصفحات HTML المعروضة على الخادم. بالنسبة إلى هذه الصفحات، يمكنك إنشاء رقم عشوائي جديد لكل ردّ.
سياسة أمان المحتوى المستندة إلى التجزئة
بالنسبة إلى سياسة CSP المستندة إلى التجزئة، تتم إضافة تجزئة كل علامة نص برمجي مضمّن إلى سياسة CSP. ولكل نص برمجي تجزئة مختلفة. لا يمكن للمهاجم تضمين ملف برمجي ضار أو تشغيله في صفحتك، لأنّه يجب أن تكون التجزئة لهذا الملف البرمجي في ملف CSP لكي يتم تشغيله.
استخدِم سياسة CSP المستندة إلى التجزئة لصفحات HTML التي يتم عرضها بشكل ثابت أو الصفحات التي يجب تخزينها مؤقتًا. على سبيل المثال، يمكنك استخدام بروتوكول CSP المستنِد إلى التجزئة لتطبيقات الويب المكوّنة من صفحة واحدة والتي تم إنشاؤها باستخدام إطارات عمل مثل Angular أو React أو غيرها، والتي يتم عرضها بشكلٍ ثابت بدون عرض من جهة الخادم.
الخطوة 2: ضبط سياسة CSP صارمة وإعداد النصوص البرمجية
عند إعداد خدمة إدارة خدمات المحتوى، تتوفّر لك بعض الخيارات:
- وضع "إعداد التقارير فقط" (
Content-Security-Policy-Report-Only
) أو وضع التنفيذ (Content-Security-Policy
): في وضع "إعداد التقارير فقط"، لن تحظر خدمة "إدارة خدمات المحتوى" موارد الويب بعد، وبالتالي لن يتعطّل أي محتوى على موقعك الإلكتروني، ولكن يمكنك الاطّلاع على الأخطاء والحصول على تقارير عن أي محتوى كان سيتم حظره. عند ضبط ملفّ سياسة الخدمات في المقام الأول، لا يهمّ هذا الأمر كثيرًا، لأنّ كلا الوضعَين يعرضان الأخطاء في وحدة تحكّم المتصفّح. على أي حال، يمكن أن يساعدك وضع التنفيذ في العثور على الموارد التي تحظرها مسودة سياسة خدمة المحتوى (CSP)، لأنّ حظر أحد الموارد يمكن أن يجعل صفحتك تبدو متعطّلة. يصبح وضع "التقارير فقط" مفيدًا جدًا في وقت لاحق من العملية (راجِع الخطوة 5). - عنوان أو علامة
<meta>
في HTML بالنسبة إلى التطوير على الجهاز، يمكن أن تكون علامة<meta>
أكثر ملاءمةً لتعديل سياسة CSP والاطّلاع بسرعة على تأثيرها في موقعك الإلكتروني. ومع ذلك:- لاحقًا، عند نشر خدمة إدارة المحتوى في مرحلة الإنتاج، ننصحك بضبطها على أنّها رأس HTTP.
- إذا كنت تريد ضبط CSP في وضع "التقارير فقط"، عليك ضبطه كملف header، لأنّ علامات CSP الوصفية لا تتيح وضع "التقارير فقط".
اضبط عنوان Content-Security-Policy
استجابة HTTP التالي
في تطبيقك:
Content-Security-Policy: script-src 'nonce-{RANDOM}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
إنشاء رقم تعريف عشوائي لبروتوكول CSP
الرمز المؤقت هو رقم عشوائي يُستخدَم مرة واحدة فقط عند تحميل الصفحة. لا يمكن لبروتوكول CSP المستنِد إلى مفتاح عشوائي تخفيف هجمات XSS إلا إذا لم يتمكّن المهاجمون من تخمين قيمة المفتاح العشوائي. يجب أن يستوفي مفتاح nonce في بروتوكول CSP الشروط التالية:
- قيمة عشوائية قوية من الناحية التشفيرية (يُفضَّل أن يكون طولها 128 بت أو أكثر)
- يتم إنشاؤها حديثًا لكل ردّ
- مشفَّر بترميز Base64
في ما يلي بعض الأمثلة على كيفية إضافة مفتاح تشفير عشوائي في إطار عمل من جهة الخادم:
- Django (python)
- Express (JavaScript):
const app = express(); app.get('/', function(request, response) { // Generate a new random nonce value for every response. const nonce = crypto.randomBytes(16).toString("base64"); // Set the strict nonce-based CSP response header const csp = `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`; response.set("Content-Security-Policy", csp); // Every <script> tag in your application should set the `nonce` attribute to this value. response.render(template, { nonce: nonce }); });
إضافة سمة nonce
إلى عناصر <script>
باستخدام تنسيق CSP المستنِد إلى مفتاح إنشاء الجلسة، يجب أن يحتوي كل عنصر <script>
على سمة nonce
تتطابق مع قيمة مفتاح إنشاء الجلسة
العشوائية المحدّدة في عنوان CSP. يمكن أن تحتوي جميع النصوص البرمجية على رمز ال nonce نفسه. الخطوة الأولى هي إضافة هذه السمات إلى جميع النصوص البرمجية حتى يسمح بها
إطار عمل CSP.
اضبط عنوان Content-Security-Policy
استجابة HTTP التالي
في تطبيقك:
Content-Security-Policy: script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
بالنسبة إلى النصوص البرمجية المضمّنة المتعددة، يكون أسلوب البنية كما يلي:
'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}'
.
تحميل النصوص البرمجية من مصادرها بشكل ديناميكي
يمكنك تحميل النصوص البرمجية التابعة لجهات خارجية ديناميكيًا باستخدام نص برمجي مضمّن.
<script> var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js']; scripts.forEach(function(scriptUrl) { var s = document.createElement('script'); s.src = scriptUrl; s.async = false; // to preserve execution order document.head.appendChild(s); }); </script>
<script src="https://example.org/foo.js"></script> <script src="https://example.org/bar.js"></script>
اعتبارات متعلقة بتحميل النصوص البرمجية
يضيف مثال النص البرمجي المضمّن s.async = false
لضمان
تنفيذ foo
قبل bar
، حتى إذا
تم تحميل bar
أولاً. في المقتطف التالي، لا تحظر s.async = false
المعالج أثناء تحميل النصوص البرمجية، لأنّ النصوص البرمجية تتم
إضافتها ديناميكيًا. لا يتوقّف المُحلِّل إلا أثناء تنفيذ النصوص البرمجية، كما هو الحال مع async
نص برمجي. ومع ذلك، عند استخدام هذا المقتطف،
يُرجى مراعاة ما يلي:
-
قد يتم تنفيذ نص برمجي واحد أو كليهما قبل انتهاء تنزيل المستند. إذا كنت تريد أن يكون المستند جاهزًا بحلول وقت تنفيذ
النصوص البرمجية، انتظِر حدث
DOMContentLoaded
قبل إلحاق النصوص البرمجية. إذا تسبّب ذلك في حدوث مشكلة في الأداء لأنّه لا يبدأ تنزيل النصوص البرمجية مبكرًا بما يكفي، استخدِم علامات التحميل المُسبَق في وقت أبكر من الصفحة. -
لا يؤدي
defer = true
أيّ وظيفة. إذا كنت بحاجة إلى هذا السلوك، يمكنك تشغيل النص البرمجي يدويًا عند الحاجة.
الخطوة 3: إعادة صياغة نماذج HTML والرمز البرمجي من جهة العميل
يمكن استخدام معالجات الأحداث المضمّنة (مثل onclick="…"
وonerror="…"
) وعناوين URL لبرامج JavaScript
(<a href="javascript:…">
) لتشغيل النصوص البرمجية. وهذا يعني أنّه يمكن للمهاجم الذي يعثر على خطأ XSS حقن هذا النوع من علامات HTML وتنفيذ رمز JavaScript ضار. تحظر سياسة CSP المستندة إلى مفتاح عشوائي أو تجزئة استخدام هذا النوع من الترميز.
إذا كان موقعك الإلكتروني يستخدم أيًا من هذه الأنماط، عليك إعادة صياغة هذه الأنماط لإنشاء بدائل أكثر أمانًا.
إذا فعّلت ميزة "أمان المحتوى في خدمة مقارنة الأسعار" في الخطوة السابقة، ستتمكّن من الاطّلاع على انتهاكات "أمان المحتوى في خدمة مقارنة الأسعار" في وحدة التحكّم في كل مرة تحظر فيها هذه الميزة نمطًا غير متوافق.
في معظم الحالات، يكون الحلّ بسيطًا:
إعادة صياغة معالِجات الأحداث المضمّنة
<span id="things">A thing.</span> <script nonce="${nonce}"> document.getElementById('things').addEventListener('click', doThings); </script>
<span onclick="doThings();">A thing.</span>
إعادة صياغة javascript:
معرّفات الموارد المنتظمة (URI)
<a id="foo">foo</a> <script nonce="${nonce}"> document.getElementById('foo').addEventListener('click', linkClicked); </script>
<a href="javascript:linkClicked()">foo</a>
إزالة eval()
من JavaScript
إذا كان تطبيقك يستخدم eval()
لتحويل عمليات تسلسل سلاسل JSON إلى ملف JS
objects، عليك إعادة صياغة هذه المثيلات إلى JSON.parse()
، وهي أيضًا
أسرع.
إذا لم تتمكّن من إزالة جميع استخدامات eval()
، سيظل بإمكانك ضبط سياسة أمان محتوى صارمة تستند إلى رمز مميّز عشوائي، ولكن عليك استخدام الكلمة الرئيسية 'unsafe-eval'
CSP، ما يجعل سياسة أمان المحتوى أقل أمانًا قليلاً.
يمكنك العثور على هذه الأمثلة وغيرها من أمثلة إعادة التنظيم هذه في الدرس التطبيقي التالي حول البرمجة في صفحات الويب (CSP):
الخطوة 4 (اختيارية): إضافة عناصر احتياطية لتتوافق مع إصدارات المتصفّح القديمة
إذا كنت بحاجة إلى إتاحة استخدام إصدارات قديمة من المتصفّح:
- يتطلب استخدام
strict-dynamic
إضافةhttps:
كخيار احتياطي لإصدارات Safari السابقة. عند إجراء ذلك:- تتجاهل جميع المتصفّحات التي تتيح
strict-dynamic
البديلhttps:
، لذلك لن يؤدي ذلك إلى تقليل قوة السياسة. - في المتصفّحات القديمة، لا يمكن تحميل النصوص البرمجية من مصادر خارجية إلا إذا كانت تأتي من
مصدر HTTPS. وهذا الإجراء أقل أمانًا من سياسة CSP الصارمة، ولكنه مع ذلك
يمنع بعض الأسباب الشائعة لهجوم XSS، مثل عمليات إدخال عناوين URL الخاصة بخدمة
javascript:
.
- تتجاهل جميع المتصفّحات التي تتيح
- لضمان التوافق مع إصدارات المتصفّح القديمة جدًا (4 سنوات أو أكثر)، يمكنك إضافة
unsafe-inline
كخيار احتياطي. تتجاهل جميع المتصفّحات الحديثةunsafe-inline
إذا كان هناك مفتاح تشفير عشوائي أو تجزئة في بروتوكول CSP.
Content-Security-Policy:
script-src 'nonce-{random}' 'strict-dynamic' https: 'unsafe-inline';
object-src 'none';
base-uri 'none';
الخطوة 5: نشر خدمة إدارة الخدمات السحابية (CSP)
بعد التأكّد من أنّ خدمة إدارة الخدمات (CSP) لا تحظر أي نصوص برمجية مشروعة في بيئة التطوير المحلية، يمكنك نشر خدمة إدارة الخدمات (CSP) في مرحلة الإعداد، ثم في بيئة الإنتاج:
- (اختياري) يمكنك نشر خدمة إدارة المحتوى في وضع "إعداد التقارير فقط" باستخدام العنوان
Content-Security-Policy-Report-Only
. يُعدّ وضع "إعداد التقارير فقط" مفيدًا لاختبار تغيير قد يؤدي إلى حدوث خلل، مثل خدمة إدارة الخدمات في مرحلة الإنتاج قبل بدء فرض قيود إدارة الخدمات في مرحلة الإنتاج. في وضع "إعداد التقارير فقط"، لا يؤثر ملفّ CSP في سلوك تطبيقك، ولكن يستمر المتصفّح في إنشاء أخطاء وحدة التحكّم وتقارير الانتهاكات عند رصد أنماط غير متوافقة مع ملفّ CSP، حتى تتمكّن من معرفة المشاكل التي كانت ستواجه المستخدمين النهائيين. لمزيد من المعلومات، يُرجى الاطّلاع على Reporting API. - عندما تكون متأكّدًا من أنّ خدمة إدارة المحتوى في أثناء النقل لن تؤدي إلى تعطُّل موقعك الإلكتروني للمستخدمين النهائيين،
يمكنك نشر خدمة إدارة المحتوى في أثناء النقل باستخدام عنوان استجابة
Content-Security-Policy
. وننصحك بضبط سياسة CSP باستخدام عنوان HTTP من جهة الخادم لأنّه أكثر أمانًا من علامة<meta>
. بعد إكمال هذه الخطوة، يبدأ إطار عمل CSP في حماية تطبيقك من هجمات XSS.
القيود
توفّر سياسة أمان المحتوى الصارمة بشكل عام طبقة أمان إضافية قوية تساعد في
الحدّ من هجمات XSS. في معظم الحالات، تقلّل سياسة أمان المحتوى (CSP) من مساحة الهجوم بشكل كبير، وذلك من خلال
رفض الأنماط الخطيرة، مثل عناوين URL الخاصة بـ javascript:
. ومع ذلك، استنادًا إلى نوع
سياسة CSP التي تستخدمها (الأرقام الخاصة أو التجزئات، مع 'strict-dynamic'
أو بدونها)، هناك
حالات لا تحمي فيها سياسة CSP تطبيقك أيضًا:
- إذا أضفت قيمة nonce إلى نص برمجي، ولكن تمّت حقن قيمة مباشرةً في النص أو في مَعلمة
src
لعنصر<script>
هذا. - إذا كانت هناك عمليات حقن في مواضع النصوص البرمجية التي تم إنشاؤها ديناميكيًا
(
document.createElement('script')
)، بما في ذلك أي دوال مكتبة تُنشئscript
عقد DOM استنادًا إلى قيم وسيطاتها ويشمل ذلك بعض واجهات برمجة التطبيقات الشائعة، مثل.html()
في jQuery، بالإضافة إلى.get()
و.post()
في الإصدارات الأقدم من jQuery 3.0. - إذا كانت هناك عمليات إدراج نماذج في تطبيقات AngularJS القديمة يمكن للمهاجم الذي يمكنه إدخال محتوى في نموذج AngularJS استخدام ذلك النموذج لتنفيذ JavaScript عشوائي.
- إذا كانت السياسة تحتوي على
'unsafe-eval'
، وعمليات حقن فيeval()
،setTimeout()
، وبعض واجهات برمجة التطبيقات الأخرى التي نادرًا ما يتم استخدامها.
على المطوّرين ومهندسي الأمان الانتباه بشكل خاص إلى مثل هذه النماذج أثناء مراجعات الرموز البرمجية وعمليات تدقيق الأمان. يمكنك العثور على مزيد من التفاصيل حول هذه الحالات في المقالة Content Security Policy: A Successful Mess Between Hardening and Mitigation.