عاجل ومؤقت: ثغرة PHP حرجة. كيف تفحص ←

كيف تصلح سياسة أمان المحتوى (CSP)

سياسة أمان المحتوى (CSP) هي أقوى ترويسات أمان HTTP — وأكثرها تعرضًا للضبط الخاطئ. إذا أشار فحص أمان نطاقك إلى سياسة CSP مفقودة أو تحتوي على unsafe-inline، فإن هذا الدليل يشرح المشكلة وكيف تصلحها كما ينبغي. وعلى خلاف معظم الترويسات، لا يمكن إصلاح CSP بنسخ قيمة واحدة. فالسياسة الصحيحة تعتمد على ما يحمّله موقعك فعلًا. ويرشدك هذا الدليل إلى بنائها بشكل صحيح.

ماذا تعني هذه النتيجة

سياسة أمان المحتوى (CSP) ترويسة استجابة HTTP تُخبر المتصفح بدقة بمصادر النصوص البرمجية والأنماط والصور والخطوط والإطارات المسموح بتحميلها في صفحتك. ويُحظر كل ما ليس في القائمة المعتمدة — مما يمنع هجمات البرمجة عبر المواقع (XSS) التي تُنفَّذ فيها نصوص برمجية خبيثة محقونة في متصفح الزائر.

يُبلغ mydomainrisk.com عن نتيجتين تخصّان CSP:

  • لا توجد CSP إطلاقًا — لا يرسل موقعك ترويسة Content-Security-Policy. ولا يفرض المتصفح أي قيود على النصوص البرمجية أو الموارد التي يمكن تنفيذها في صفحاتك.
  • تحتوي CSP على unsafe-inline — سياستك موجودة لكنها تتضمن 'unsafe-inline' في التوجيه script-src أو style-src. وهذا يطلب من المتصفح السماح بأي نص برمجي أو نمط مضمَّن في الصفحة — أي أن المهاجم القادر على حقن وسم <script> واحد في HTML الخاص بك يستطيع تنفيذ أي شيفرة JavaScript. وهذا يُفرغ وجود CSP من معظم جدواه.

لماذا يهم ذلك

تبقى XSS باستمرار من أكثر ثغرات الويب استغلالًا. وسياسة CSP المضبوطة جيدًا هي أنجع دفاع منفرد على جانب المتصفح — إذ تحدّ مما يستطيع المهاجم فعله حتى لو تمكّن من حقن شيفرة في صفحتك. ومن دونها، يمكن لهجوم XSS ناجح أن يسرق ملفات تعريف ارتباط الجلسة، أو يعيد توجيه المستخدمين، أو يسرّب البيانات، أو يشوّه موقعك.

unsafe-inline هو أكثر أخطاء CSP شيوعًا. يضيفه كثير من المطورين لمنع تعطّل نصوصهم البرمجية المضمَّنة بدلًا من إصلاح الشيفرة الأساسية — لكنه يزيل أهم حماية توفرها CSP.

الجزء ١ — لماذا لا يمكنك ببساطة نسخ قيمة CSP

على خلاف HSTS أو X-Content-Type-Options، لا توجد قيمة CSP صحيحة تصلح للجميع. فالسياسة التي تعمل تمامًا لموقع HTML ثابت ستعطّل تطبيق React. والسياسة التي تعمل مع نظام إدارة محتوى (CMS) ستحظر موارد في نظام آخر.

إذا نسخت سياسة CSP من دليل أو من موقع آخر وطبّقتها على موقعك، فسيحدث أحد أمرين:

  • تكون صارمة أكثر من اللازم فتعطّل موقعك (لا تُحمَّل الصور، وتفشل النصوص البرمجية، وتختفي الخطوط)
  • تكون متساهلة أكثر من أن توفر حماية حقيقية

الطريقة الصحيحة هي أن تراقب أولًا، ثم تطبّق — باستخدام وضع report-only المدمج في CSP.

الجزء ٢ — ابنِ سياسة CSP باستخدام وضع report-only

الخطوة 1: انشر أولًا سياسة في وضع report-only

بدلًا من Content-Security-Policy، استخدم Content-Security-Policy-Report-Only. تطلب هذه الترويسة من المتصفح تقييم سياستك والإبلاغ عن المخالفات — دون حظر أي شيء. ويستمر موقعك في العمل بشكل طبيعي بينما تجمع البيانات.

ابدأ بسياسة صارمة عمدًا في وضع report-only:

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-src 'none'; report-uri /csp-report

الخطوة 2: أعِدّ نقطة استقبال لتقارير CSP (اختياري لكنه موصى به)

يرسل التوجيه report-uri تقارير المخالفات كطلبات POST بصيغة JSON إلى نقطة استقبال تحددها. ويمكنك استخدام خدمة مجانية مثل report-uri.com لجمع هذه التقارير وعرضها دون بناء نقطة استقبال خاصة بك. أو افتح أدوات المطوّر (DevTools) ← Console وابحث عن أخطاء مخالفات CSP — فهي تخبرك بدقة بالمورد الذي حُظر ومن أي مصدر.

الخطوة 3: تصفّح موقعك بالكامل

بعد نشر سياسة report-only، تنقّل في كل صفحة من صفحات موقعك — بما فيها المناطق التي تتطلب تسجيل الدخول، ومسارات الدفع، والخرائط المضمَّنة، وأدوات الدردشة، ومشغّلات الفيديو، وأي تكاملات مع جهات خارجية. وسيولّد كل مورد محظور تقريرًا بالمخالفة.

الخطوة 4: فسّر المخالفات

تخبرك كل مخالفة بالتوجيه الذي خولف (مثل script-src) والمصدر الذي حُظر (مثل https://cdn.jsdelivr.net). أضف كل مصدر مشروع إلى التوجيه المناسب:

script-src 'self' https://cdn.jsdelivr.net;

الخطوة 5: ابنِ السياسة المطبَّقة

عندما تتوقف المخالفات عن الظهور للموارد المشروعة، تصبح سياستك جاهزة للتطبيق. انتقل من Content-Security-Policy-Report-Only إلى Content-Security-Policy.

الجزء ٣ — إزالة unsafe-inline

إذا كان موقعك يستخدم حاليًا unsafe-inline وتريد إزالته، فالتحدي أن النصوص البرمجية والأنماط المضمَّنة تحتاج إلى آلية بديلة. وهناك خياران:

الخيار أ: قيم nonce (موصى به للمواقع الديناميكية)

قيمة nonce رمز عشوائي يُستخدم مرة واحدة ويُولَّد لكل طلب صفحة. تضيفه إلى ترويسة CSP وإلى كل وسم نص برمجي مضمَّن.

في ترويسة CSP:

Content-Security-Policy: script-src 'self' 'nonce-RANDOM_BASE64_VALUE'

في كل وسم نص برمجي مضمَّن:

<script nonce="RANDOM_BASE64_VALUE">
  // your inline script
</script>

يجب أن تكون قيمة nonce عشوائية تشفيريًا (١٢٨ بت على الأقل)، ومختلفة في كل تحميل للصفحة، وألا يُعاد استخدامها أبدًا. ومعظم أطر العمل على جانب الخادم (Next.js وDjango وLaravel وRails) تدعم nonce بشكل مدمج أو بسهولة.

الخيار ب: قيم التجزئة (موصى به للمواقع الثابتة)

للنصوص البرمجية المضمَّنة التي لا تتغير أبدًا، يمكنك السماح بها عبر قيمة تجزئة SHA-256 الخاصة بها بدلًا من استخدام nonce:

Content-Security-Policy: script-src 'self' 'sha256-BASE64_HASH_OF_SCRIPT_CONTENT'

ولّد قيمة التجزئة باستخدام:

echo -n 'your inline script content here' | openssl dgst -sha256 -binary | openssl base64

الجزء ٤ — مرجع توجيهات CSP الشائعة

التوجيهما يتحكم فيه
default-srcالقيمة الاحتياطية لكل أنواع الموارد غير المذكورة صراحةً
script-srcمصادر JavaScript
style-srcمصادر CSS
img-srcمصادر الصور
font-srcمصادر خطوط الويب
connect-srcاتصالات Fetch وXHR وWebSocket
frame-srcمصادر iframe
object-srcمحتوى الإضافات (Flash وغيرها) — اضبطه على 'none'
base-uriيقيّد وسم <base> — اضبطه على 'self'
form-actionالوجهات التي يمكن للنماذج الإرسال إليها

قيم آمنة شائعة: 'self' — المصدر نفسه فقط · 'none' — حظر تام · https: — أي مصدر HTTPS · data: — عناوين data URI (مطلوبة عادةً لـimg-src)

الجزء ٥ — انشر الترويسة بحسب المنصة

Cloudflare (Transform Rules)

Security → Transform Rules → Modify Response Header → Add rule ← اضبط Content-Security-Policy على قيمة سياستك.

Nginx

add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'" always;

Apache

Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'"

Vercel (vercel.json)

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Content-Security-Policy",
          "value": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'"
        }
      ]
    }
  ]
}

Netlify (netlify.toml)

[[headers]]
  for = "/*"
  [headers.values]
    Content-Security-Policy = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'"

تتضمن القيم المثالية أعلاه 'unsafe-inline' في style-src لأن كثيرًا من المواقع تحمّل أنماطًا مضمَّنة عبر أطر عمل CSS. احذفه إذا كان موقعك لا يستخدم أنماطًا مضمَّنة، واستبدل به قيم nonce أو قيم تجزئة.

تحقق من نجاح الإجراء

افحص نطاقك على mydomainrisk.com — ستتحدّث نتيجة CSP لتبيّن ما إذا كانت سياستك موجودة وما إذا كان unsafe-inline قد أُزيل من script-src. ويكتشف الفحص أيضًا ما إذا كانت CSP في وضع التطبيق أو في وضع report-only ويشير إلى الفرق. وإن كنت تستخدم وضع report-only عمدًا ريثما تحسّن سياستك، فلا بأس — إذ ستذكر النتيجة ذلك بدلًا من اعتباره إخفاقًا.

هل لا يزال unsafe-inline يظهر بعد نشر الإصلاح؟

تحقق من:

  • أن الترويسة تُرسَل في الاستجابة الصحيحة (تحقق عبر DevTools ← Network ← انقر على صفحتك ← Response Headers)
  • عدم وجود سياسة CSP متعارضة مضبوطة في مكان آخر (مثل شبكة CDN تضيف ترويسة خاصة بها)
  • أن قيم nonce تُحقن فعلًا في وسوم النصوص البرمجية المضمَّنة عند عرض الصفحة

افحص سياسة أمان المحتوى (CSP)

يكتشف MyDomainRisk ما إذا كانت CSP موجودة ومطبَّقة وخالية من unsafe-inline — ويُدرجها في تقييم المخاطر الإجمالي.

افحص نطاقًا مجانًا ←