في هذا الدرس 6تصفّح المواضيع
تعتمد هجمات تزوير الطلبات عبر المواقع (CSRF) على خداع متصفح المستخدم المسجل دخوله لإرسال طلب تغييري غير مقصود (كتغيير البريد أو تحويل رصيد) إلى تطبيق PHP الخاص بك.
- ترفق المتصفحات كوكيز الجلسة (
PHPSESSID) تلقائياً مع الطلبات المرسلة للموقع ما لم تُضبط طبقات الحماية. - بدون توكن مزامنة سري مرتبط بالجلسة، لا يستطيع خادم PHP التفرقة بين نموذج أرسله المستخدم بإرادته ونموذج مخفي أُرسل من موقع خارجي.
- لا تنفذ أبداً أي عمليات تغيير بيانات (إضافة أو تعديل أو حذف أو خروج) عبر طلبات
GET.
يُعد فرض طريقة POST الشرط الأول للحماية من CSRF، لأن روابط GET يمكن تشغيلها بمجرد وضعها داخل وسم صورة <img src="...">.
<?php
$method = "GET";
$action = "delete_account";
// NEVER allow state-changing actions via GET!
if ($method !== "POST") {
http_response_code(405);
echo "405 Method Not Allowed: Use POST for $action\n";
}
يجب أن يكون توكن CSRF مستحيل التوقع؛ لذا ولّده دائماً عبر مولد البايتات العشوائية التشفيري في PHP (random_bytes(32)) مع تحويله لنص سداسي عشري بدالة bin2hex().
- تُنتج العبارة
bin2hex(random_bytes(32))سلسلة سداسية عشرية بطول 64 محرفاً وبقوة عشوائية تبلغ 256 بت. - خزّن التوكن المولد داخل
$_SESSION["csrf_token"]ليبقى مرتبطاً حصرياً بجلسة المستخدم الحالية على الخادم. - لا تستخدم أبداً الدوال القابلة للتوقع مثل
rand()أوmt_rand()أوuniqid()أوtime()لتوليد التوكنات الأمنية.
تسحب دالة random_bytes(32) اثنيْن وثلاثين بايتاً عشوائياً تشفيرياً من نواة نظام التشغيل، وتحولها bin2hex() إلى نص آمن من 64 محرفاً.
<?php
$_SESSION = [];
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['csrf_token'];
echo "Token Length : " . strlen($token) . " hex chars\n";
echo "Valid Hex? : " . (ctype_xdigit($token) ? "Yes" : "No") . "\n";
يجب أن يتضمن كل نموذج <form method="POST"> في تطبيقك حقل إدخال مخفياً يحمل قيمة توكن CSRF الخاص بالجلسة الحالية.
- أدرج الحقل
<input type="hidden" name="csrf_token" value="...">داخل كل نموذج يُجري تغييراً على البيانات. - رمّز قيمة التوكن دائماً عبر
htmlspecialchars($token, ENT_QUOTES, "UTF-8")كقاعدة ثابتة في قوالب العرض. - أنشئ دالة مساعدة مختصرة مثل
csrfField()لتضمين الحقل المخفي بسهولة في جميع النماذج دون نسيان.
عندما يرسل المستخدم النموذج، يبعث المتصفح القيمة $_POST["csrf_token"] ضمن جسم الطلب جنباً إلى جنب مع باقي الحقول.
<?php
function csrfField(string $token): string {
$safe = htmlspecialchars($token, ENT_QUOTES, 'UTF-8');
return '<input type="hidden" name="csrf_token" value="' . $safe . '">';
}
$sampleToken = "a9f4c2e8b1d07365";
echo csrfField($sampleToken) . "\n";
عند وصول طلب POST، تحقق من تطابق التوكن المرسل مع توكن الجلسة باستخدام الدالة hash_equals($knownSessionToken, $userSubmittedToken) بدلاً من المعامل ===.
- يتوقف المعامل التقليدي
===عند أول بايت غير متطابق، مما قد يسرب فروقاً زمنية دقيقة. - تقارن دالة
hash_equals()جميع البايتات في زمن ثابت عندما يكون النصان متساويين في الطول. - تأكد دائماً من كون المدخل نصاً عبر
(string) ($_POST["csrf_token"] ?? "")قبل تمريره إلىhash_equals()لتجنب خطأTypeErrorفي PHP 8.
يضمن فحص $sessionToken === "" أولاً عدم تطابق جلسة فارغة مع مدخل فارغ، ثم تجري hash_equals() المقارنة الآمنة في زمن ثابت.
<?php
$_SESSION['csrf_token'] = "4f8b2e9a1c7d60354f8b2e9a1c7d6035";
$_POST['csrf_token'] = "4f8b2e9a1c7d60354f8b2e9a1c7d6035";
$sessionToken = $_SESSION['csrf_token'] ?? '';
$postToken = (string) ($_POST['csrf_token'] ?? '');
if ($sessionToken === '' || !hash_equals($sessionToken, $postToken)) {
http_response_code(403);
exit("403 CSRF Rejected\n");
}
echo "CSRF Token Verified Safely!\n";
تساعد إضافة طابع زمني لانتهاء الصلاحية (TTL) وتدوير التوكن بعد العمليات الحساسة على تقليص نافذة الخطر إلى الحد الأدنى.
- احفظ وقت الإنشاء
$_SESSION["csrf_time"] = time()مع التوكن، وارفض التوكنات التي يتجاوز عمرها مدة الصلاحية المحددة (مثل 3600 ثانية). - أعد توليد
$_SESSION["csrf_token"]فور تسجيل دخول المستخدم أو خروجه أو تغيير كلمة مروره. - فضّل التوكن المرتبط بالجلسة مع وقت صلاحية على التوكن أحادي الاستخدام الصارم لتفادي تعطل النماذج عند فتح المستخدم لعدة تبويبات.
عندما يتجاوز الفارق الزمني $now - $_SESSION["csrf_time"] حد 3600 ثانية، يُرفض التوكن القديم ويُستبدل بتوكن عشوائي جديد.
<?php
$now = 1762550000;
$_SESSION['csrf_token'] = "9a8b7c6d5e4f";
$_SESSION['csrf_time'] = $now - 7200; // Created 2 hours ago
$ttl = 3600;
$isExpired = ($now - $_SESSION['csrf_time']) > $ttl;
if ($isExpired) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(16));
$_SESSION['csrf_time'] = $now;
echo "Token expired (> 3600s) -> Rotated!\n";
}
اجمع بين توكنات المزامنة CSRF وخاصية SameSite للكوكيز وفحص طريقة الطلب HTTP لتحقيق حماية دفاعية متعددة الطبقات.
- اعتمد على توكن المزامنة (
random_bytesمعhash_equals) كخط الدفاع الأساسي ضد CSRF. - اضبط كوكيز الجلسة بالخيار
"samesite" => "Lax"أو"Strict"كطبقة حماية إضافية من جهة المتصفح. - ارفض أي طلب غير مرسل بطريقة
POSTعلى مسارات تعديل البيانات برمز الحالة405 Method Not Allowed.
يتحقق حارس verifyCsrf() من عدم فراغ توكن الجلسة، وسلامة نوع النص، والتطابق الثابت زمنياً في ثلاثة أسطر.
| طبقة الحماية | الأداة في PHP | الخطر الذي تمنعه |
|---|---|---|
| CSPRNG Token Generation | bin2hex(random_bytes(32)) | Token guessing & brute-forcing |
| Timing-Safe Comparison | hash_equals($sess, $post) | Timing side-channel leaks |
| Token TTL & Rotation | $_SESSION["csrf_time"] | Stale or replayed tokens |
| Cookie Policy | samesite => "Lax" | Cross-site cookie attachment on POST |
<?php
function verifyCsrf(string $sessToken, mixed $postInput): bool {
if ($sessToken === '' || !is_string($postInput)) {
return false;
}
return hash_equals($sessToken, $postInput);
}
$ok = verifyCsrf("abc123def456", "abc123def456");
echo "Defense check: " . ($ok ? "PASSED" : "BLOCKED") . "\n";
ملخص الدرس
حصاد الدرسمراجعة مركّزة لأهم المفاهيم والقواعد الهندسية التي أتقنتها في هذا الدرس قبل التطبيق العملي
ماذا تعلّمت في هذا الدرس؟
اجمع بين توكنات المزامنة CSRF وخاصية SameSite للكوكيز وفحص طريقة الطلب HTTP لتحقيق حماية دفاعية متعددة الطبقات. اعتمد على توكن المزامنة (random_bytes مع hash_equals) كخط الدفاع الأساسي ضد CSRF. اضبط كوكيز الجلسة بالخيار "samesite" => "Lax" أو "Strict" كطبقة حماية إضافية من جهة المتصفح. ارفض أي طلب غير مرسل بطريقة POST على مسارات تعديل البيانات برمز الحالة 405 Method Not Allowed.
سجّل الدخول لحفظ تقدمك
تابع إنجاز المواضيع والمصطلحات والمهام، وكمّل من آخر خطوة على أي جهاز.
المصطلحات4
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 CSRF Protection الحماية من CSRF
- احمِ النماذج والعمليات الحساسة من هجمات تزوير الطلبات عبر المواقع CSRF باستخدام random_bytes والمقارنة الثابتة زمنياً hash_equals.
- للمراجعة
- 02 How CSRF Exploits Active Sessions كيف تستغل هجمات CSRF الجلسات النشطة
- المفهوم الأساسي لقسم كيف تستغل هجمات CSRF الجلسات النشطة في لغة PHP الحديثة.
- للمراجعة
- 03 Generating Tokens with random_bytes توليد توكنات تشفيرية آمنة بـ random_bytes
- الصياغة البرمجية والتطبيق العملي المباشر لقسم توليد توكنات تشفيرية آمنة بـ random_bytes.
- للمراجعة
- 04 Embedding CSRF Tokens in HTML Forms تضمين توكن CSRF كحقل مخفي في النماذج
- قواعد التحقق وأفضل الممارسات القياسية الخاصة بقسم تضمين توكن CSRF كحقل مخفي في النماذج.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
الاختصارات4
راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.
- 01 PHP PHP
- PHP: Hypertext Preprocessor
- لغة البرمجة النصية على جانب الخادم لبناء تطبيقات الويب الديناميكية وواجهات البرمجة.
- للمراجعة
- 02 CLI CLI
- Command-Line Interface
- واجهة سطر الأوامر التي تتيح تشغيل ملفات PHP مباشرة عبر الطرفية.
- للمراجعة
- 03 FPM FPM
- FastCGI Process Manager
- مدير عمليات FastCGI عالي الأداء لمعالجة طلبات خادم الويب في PHP.
- للمراجعة
- 04 HTML HTML
- HyperText Markup Language
- لغة الترميز القياسية التي يولدها محرك PHP ويرسلها إلى متصفح العميل بعد التنفيذ.
- للمراجعة
لا توجد بطاقات تطابق البحث والتصفية.
اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.
مهامك قبل الاختبار
خطوة صغيرة كل مرة. وإنجاز تقدر ترجع له.
-
أنشئ ملف PHP محلياً ونفّذ المثال البرمجي المشروح في قسم كيف تستغل هجمات CSRF الجلسات النشطة للتحقق من المخرجات.
-
بدّل بين الحالات الثلاث في المحاكي التفاعلي ضمن قسم توليد توكنات تشفيرية آمنة بـ random_bytes وقارن مخرجات التنفيذ.
تأشير المهمة إقرار منك بإتمامها، وليس تصحيحًا آليًا للكود. يُحفظ تقدمك في حسابك.
اختبر فهمك.
ووصلها للإتقان.
كل سؤال خطوة لقدّام. اختار، اكتب كود، وشوف نتيجتك فورًا.
أكمل المهام، واجمع كل الإجابات الصحيحة. أقل من 100%؟ راجع وأعد المحاولة.
الأسئلة متاحة للاطلاع. سجّل الدخول للتفاعل وحفظ تقدمك.
لمحة عن التحدي8 أسئلة تنتظرك +
لماذا يمكن أن ينجح هجوم CSRF إذا كان ملف معالجة النموذج في PHP يفحص $_SESSION["user_id"] فقط؟
تثبت كوكيز الجلسة هوية المتصفح لكنها لا تثبت أن المستخدم قصد إرسال ذلك النموذج تحديداً.
أي تعبير في PHP يُوصى به لتوليد توكن CSRF عشوائي آمن تشفيرياً بطول 64 محرفاً؟
تولد random_bytes(32) بايتات عشوائية تشفيرية وتحولها bin2hex() إلى 64 محرفاً سداسياً عشرياً.
أين يجب تخزين النسخة المرجعية لتوكن المزامنة CSRF على جانب الخادم؟
حفظ التوكن في $_SESSION يربطه بجلسة المستخدم الحالية على الخادم.
لماذا يجب استخدام دالة hash_equals($sessionToken, $postToken) بدلاً من === عند التحقق من توكن CSRF؟
تستغرق المقارنة نفس الزمن بغض النظر عن موقع أول حرف مختلف.
كيف يتم تمرير توكن CSRF عادةً داخل نموذج HTML يرسل بطريقة POST؟
يرسل الحقل المخفي قيمة التوكن ضمن جسم طلب POST دون إزعاج المستخدم.
لماذا يجب عدم تنفيذ العمليات المغيرة للبيانات (مثل حذف سجل) عبر طلبات HTTP GET مطلقاً؟
يجب أن تكون طلبات GET للقراءة فقط دون إحداث تغييرات جانبية.
ماذا يحدث في PHP 8 إذا مررت القيمة null أو مصفوفة كمعامل ثانٍ لدالة hash_equals()؟
يجب دائماً التحقق بـ is_string() أو التحويل النصي قبل استدعاء الدالة.
أي خاصية في الكوكيز توفر طبقة دفاع إضافية من جهة المتصفح ضد إرسال النماذج العابرة للمواقع؟
تمنع SameSite=Lax إرسال الكوكيز مع طلبات POST العابرة للمواقع.