الـ Input · فصل ٢

XSS — سكريبت غريب جوه صفحتك

Stored / Reflected / DOM XSS · output encoding · dangerouslySetInnerHTML · CSP
🎯 الهدف: تعرف أنواع الـ XSS، وليه React بيحميك في أغلب الوقت، والأماكن اللي بيسيبك فيها، وتضيف CSP كطبقة تانية.

الفكرة في سطر

الـ Cross-Site Scripting معناه إن JavaScript مش بتاعك بيشتغل جوه صفحتك، بصلاحيات موقعك: يقرا الصفحة، يبعت requests باسم المستخدم، يسرق tokens متخزنة في localStorage، أو يغيّر الفورم.

التلات أنواع

النوعالـ payload بيتخزن فين؟مثال
Storedفي الـ databaseكومنت أو bio بيتعرض لكل الزوار
Reflectedفي الـ request نفسهبحث بيعرض ?q= في الصفحة من غير encoding
DOM-basedفي الـ frontend بسJS بياخد location.hash ويحطه في innerHTML

React بيحميك… لحد نقطة

React بيعمل escape لأي نص بتحطه بين {}. يعني لو الكومنت فيه tag، هيظهر كنص مش كـ HTML. الثغرات بتيجي من الأبواب الخلفية:

Comment.jsx
// ✅ آمن: React بيعمل escape<p>{comment.body}</p>// ❌ HTML خام من المستخدم<p dangerouslySetInnerHTML={{ __html: comment.body }} />// ❌ لينك المستخدم كتبه: ممكن يبدأ بـ javascript:<a href={user.website}>الموقع</a>
SafeHtml.jsx
import DOMPurify from "dompurify";export function SafeHtml({ html }) {  const clean = DOMPurify.sanitize(html);  return <div dangerouslySetInnerHTML={{ __html: clean }} />;}export function safeUrl(url) {  try {    const u = new URL(url);    return ["https:", "http:"].includes(u.protocol) ? u.href : "#";  } catch {    return "#";  }}

بره React نفس القاعدة: textContent بدل innerHTML، والـ template engines على السيرفر (EJS و Handlebars) استخدم الصيغة اللي بتعمل escape، مش الـ raw.

الطبقة التانية: Content-Security-Policy

الـ CSP header بيقول للمتصفح: “السكريبتات المسموحة جاية من هنا بس”. لو حد نجح يحقن tag، المتصفح يرفض ينفذه.

server.js
import helmet from "helmet";app.use(  helmet.contentSecurityPolicy({    directives: {      defaultSrc: ["'self'"],      scriptSrc: ["'self'"],          // من غير 'unsafe-inline'      objectSrc: ["'none'"],      baseUri: ["'self'"],      frameAncestors: ["'none'"],     // هنرجعله في Clickjacking    },  }),);

خلّصت الفصل لما

🧠 اختبر نفسك
١) بتعرض اسم المستخدم بين أقواس JSX. محتاج sanitize؟
لأ، React بيعمل escape للنص لوحده.
٢) CSP بيغني عن الـ escaping؟
لأ. هو طبقة دفاع تانية لو الأولى فشلت.
٣) ليه لينك المستخدم ممكن يبقى خطر في React؟
لأن href ممكن يبقى javascript: ويتنفذ لما حد يدوس.