المتصفح · فصل ٣

CSRF و Clickjacking — حد بيستخدم جلستك من غير ما تحس

CSRF · SameSite cookies · anti-CSRF token · Clickjacking · frame-ancestors · X-Frame-Options
🎯 الهدف: تفهم إزاي موقع تاني يقدر يبعت request باسمك أو يخليك تدوس على زرار مخفي، وتقفل الاتنين بالـ cookies والـ headers الصح.

الهجمتين دول مش محتاجين يسرقوا أي حاجة. هما بيستغلوا إن المتصفح بتاعك عامل login فعلًا على موقعك.

CSRF: موقع تاني بيبعت باسمك

المتصفح تاريخيًا بيبعت الـ cookies مع أي request رايح لموقعك، حتى لو الـ request ده طالع من صفحة موقع تاني. فصفحة شريرة ممكن يبقى فيها فورم مخفي بيعمل submit لوحده على POST /transfer في موقع البنك، والبنك يشوف cookie الجلسة سليمة.

الحل الأول: SameSite cookies

session.js
res.cookie("sid", sessionId, {  httpOnly: true,   // الـ JS ميقدرش يقراها (حماية من XSS)  secure: true,     // HTTPS بس  sameSite: "lax",  // متتبعتش مع POST جاي من موقع تاني  path: "/",});
القيمةبتتبعت مع requests من مواقع تانية؟
Strictلأ خالص، حتى لو داس على لينك لموقعك
Laxمع التنقل العادي بـ GET بس (لينك)، مش مع POST ولا fetch
Noneأيوه دايمًا، ولازم معاها Secure

الحل التاني: anti-CSRF token

السيرفر بيدي كل جلسة قيمة عشوائية سرية، والفورم أو الـ fetch لازم يبعتها في header أو field. الموقع التاني ميقدرش يقرا القيمة دي، فميقدرش يبعتها.

api.ts
// الـ frontend بيقرا الـ token (من meta tag أو endpoint) ويبعته مع كل تغييرawait fetch("/api/transfer", {  method: "POST",  credentials: "include",  headers: { "Content-Type": "application/json", "X-CSRF-Token": csrfToken },  body: JSON.stringify({ to, amount }),});

السيرفر يقارن الـ header بالقيمة المتخزنة في الجلسة، ولو مختلفين يرجّع 403. أغلب الـ frameworks فيها middleware جاهز للنقطة دي، راجع الـ docs بتاع الـ framework بتاعك.

Clickjacking: زرار مخفي تحت إيدك

الموقع الشرير بيحط موقعك في iframe شفاف فوق زرار مغري. إنت بتدوس على اللي شايفه، والضغطة بتروح للزرار الحقيقي اللي تحته، وإنت عامل login.

الحل: امنع موقعك يتحط في frame

server.js
// الحديث: CSPres.setHeader("Content-Security-Policy", "frame-ancestors 'none'");// القديم: للمتصفحات اللي مش بتفهم frame-ancestorsres.setHeader("X-Frame-Options", "DENY");

لو موقعك محتاج يتعرض في iframe من domain معيّن بتاعك، استخدم frame-ancestors 'self' https://app.example.com بدل 'none'. الـ helmet في Express بيحط X-Frame-Options افتراضي.

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

🧠 اختبر نفسك
١) عندك GET /account/delete والـ cookie SameSite=Lax. أمان؟
لأ. Lax بيسمح بالـ GET من التنقل العادي. خليه DELETE أو POST مع token.
٢) الـ CORS بيمنع CSRF؟
مش لوحده. الـ CORS بيمنع الموقع التاني يقرا الرد، بس الفورم العادي ممكن يتبعت ويتنفذ.
٣) إزاي تمنع موقعك يتعرض في iframe خالص؟
frame-ancestors 'none' في CSP، ومعاه X-Frame-Options: DENY.