منع XSS: ترميز المخرجات وCSP
افهم XSS المنعكس والمخزّن والقائم على DOM، واهرب المخرجات التي يتحكم فيها المستخدم، وتجنب innerHTML مع البيانات غير الموثوقة، وأضف ترويسة Content Security Policy
منع XSS: ترميز المخرجات وCSP درس مجاني في Frontend Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Frontend Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Frontend Academy 4 دروس في المجموع.
ما هي XSS؟
تحدث Cross-Site Scripting عندما يحقن المهاجم JavaScript قابلًا للتنفيذ في صفحتك. ويشغّله المتصفح بصلاحيات المصدر الخاص بك، مما قد يؤدي إلى سرقة ملفات تعريف الارتباط واختطاف الجلسات وتشويه الصفحة وإعادة التوجيه إلى برمجيات خبيثة.
الأنواع الثلاثة لـ XSS
المخزّنة: يُحفظ البرنامج النصي الخبيث في قاعدة البيانات ويُرسل إلى كل زائر. المنعكسة: تُعاد المدخلات في الاستجابة (?q=<script>...). المعتمدة على DOM: تكتب JavaScript في جهة العميل بيانات غير موثوقة داخل DOM.
السبب الجذري: استخدام innerHTML مع بيانات غير موثوقة
يُعد استخدام innerHTML (أو v-html أو dangerouslySetInnerHTML) مع سلاسل يتحكم فيها المستخدم متجه XSS رقم 1.
// BAD: user input rendered as HTML
el.innerHTML = userComment; // attacker submits <img onerror=alert(1)>
// Vue equivalent:
<div v-html="userComment"></div> // same bug
// React equivalent:
<div dangerouslySetInnerHTML={{ __html: userComment }} /> // same bugترميز المخرجات (الافتراضي الآمن)
تعامل مع السلاسل غير الموثوقة كنص، لا كـ HTML. تطبّق React وVue وAngular وSvelte هذا افتراضيًا في الاستيفاء {value}.
// SAFE: text content, not HTML
el.textContent = userComment; // <script> shows as literal text
// React (auto-escaped):
<div>{userComment}</div>
// Vue (auto-escaped):
<div>{{ userComment }}</div>تنقية HTML عند ضرورة عرضه
إذا كنت تحتاج فعلًا إلى عرض HTML من المستخدم، كما في محررات النصوص المنسّقة، فنقّه باستخدام DOMPurify — ولا تكتب أداة تنقية خاصة بك باستخدام تعبيرات نمطية.
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userHtml);
el.innerHTML = clean; // strips <script>, on* handlers, dangerous URLsالتحقق من عناوين URL
قد تتسبب عناوين URL التي يتحكم فيها المستخدم والموجودة في href أو src في XSS عبر مخططات javascript:.
// BAD:
<a href={userUrl}>Click</a>
// attacker submits: javascript:alert(1)
// GOOD: whitelist protocols:
function safeUrl(url) {
if (!/^https?:\/\//.test(url) && !url.startsWith('/')) return '#';
return url;
}سياسة أمان المحتوى (CSP)
CSP رأس استجابة HTTP يخبر المتصفح بمصادر البرامج النصية والأنماط والصور وغير ذلك المسموح بها. وحتى إذا حقن المهاجم برنامجًا نصيًا، يرفض المتصفح تشغيله.
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.trusted.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:CSP صارمة باستخدام Nonces
بالنسبة إلى البرامج النصية المضمّنة التي تتحكم فيها، استخدم nonce منفصلًا لكل طلب. ولن تحتوي البرامج النصية التي يحقنها المهاجم على nonce المطابق.
// Server generates a random nonce per request:
const nonce = crypto.randomBytes(16).toString('base64');
res.setHeader('Content-Security-Policy', `script-src 'nonce-${nonce}' 'strict-dynamic'`);
// Inject into the HTML:
<script nonce="abc123">/* trusted inline code */</script>ملفات تعريف الارتباط HttpOnly + Secure
اضبط ملفات تعريف ارتباط الجلسة باستخدام HttpOnly حتى لا تتمكن JavaScript من قراءتها — فحتى عند حدوث XSS، لن يستطيع المهاجم الحصول على رمز الجلسة.
Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Strictسلامة الموارد الفرعية (SRI)
عند تحميل البرامج النصية التابعة لجهات خارجية عبر CDN، استخدم SRI للتحقق من توافق المحتوى مع قيمة hash. ولن يتمكن CDN المخترَق من تقديم برنامج نصي ضار من دون إبطال فحص hash.
<script
src="https://cdn.example.com/lib.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"
></script>Trusted Types (تجريبية)
Trusted Types هي ميزة في المتصفح (المبني على Chromium) تُجبر نقاط الإدخال الخطرة مثل innerHTML على استقبال كائن من نوع Trusted Type، وبذلك تمنع إدراج السلاسل النصية فيها من الأساس.
اختبار XSS
حاول حقن <img src=x onerror=alert(1)> في كل حقل نصي تتم إعادة عرضه. إذا ظهر تنبيه، فهناك ثغرة XSS. استخدم أدوات مثل OWASP ZAP لإجراء عمليات فحص آلية.
فحص سريع
لماذا يكون {userInput} في React آمنًا افتراضيًا، بينما لا تكون dangerouslySetInnerHTML كذلك؟
مراجعة: الوقاية من XSS
اجعل عرض النص هو الخيار الافتراضي (فـ React's {x} وVue's {{ x }} آمنان). نظّف المحتوى باستخدام DOMPurify عند عرض HTML. اسمح بقائمة محددة من بروتوكولات URL. تمنع ترويسة CSP، مع استخدام nonces أو 'strict-dynamic'، تشغيل البرامج النصية المحقونة. تمنع ملفات تعريف الارتباط HttpOnly سرقتها عبر JavaScript. استخدم قيم hash لبرامج CDN النصية. اختبر المدخلات باستخدام payloads التقليدية.
الأسئلة الشائعة
هل درس «منع XSS: ترميز المخرجات وCSP» مجاني؟
نعم — نص درس «منع XSS: ترميز المخرجات وCSP» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Frontend Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Frontend Academy 4 دروس في المجموع.
ماذا ستتعلم في «منع XSS: ترميز المخرجات وCSP»؟
افهم XSS المنعكس والمخزّن والقائم على DOM، واهرب المخرجات التي يتحكم فيها المستخدم، وتجنب innerHTML مع البيانات غير الموثوقة، وأضف ترويسة Content Security Policy تتمرن على Frontend Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Frontend Academy؟
لا تُشترط خبرة سابقة. Frontend Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «منع XSS: ترميز المخرجات وCSP»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Frontend Academy هذا؟
نعم. كل درس في Frontend Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- منع XSS: ترميز المخرجات وCSP
- CSRF: ملفات تعريف الارتباط والرموز SameSite
- Content Security Policy: nonce وhash
- تدفقات OAuth من الواجهة الأمامية