الهجمتين دول مش محتاجين يسرقوا أي حاجة. هما بيستغلوا إن المتصفح بتاعك عامل login فعلًا على موقعك.
CSRF: موقع تاني بيبعت باسمك
المتصفح تاريخيًا بيبعت الـ cookies مع أي request رايح لموقعك، حتى لو الـ request ده طالع من صفحة موقع تاني. فصفحة شريرة ممكن يبقى فيها فورم مخفي بيعمل submit لوحده على POST /transfer في موقع البنك، والبنك يشوف cookie الجلسة سليمة.
الحل الأول: SameSite cookies
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. الموقع التاني ميقدرش يقرا القيمة دي، فميقدرش يبعتها.
// الـ 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
// الحديث: 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. أمان؟
٢) الـ CORS بيمنع CSRF؟
٣) إزاي تمنع موقعك يتعرض في iframe خالص؟
frame-ancestors 'none' في CSP، ومعاه X-Frame-Options: DENY.