انتقل إلى محتوى الدرس
الدرس 86
دورة PHP

الحماية من ثغرات تزوير الطلبات CSRF وتوليد التوكنات الآمنة في PHPPHP CSRF Protection and Secure Tokens

احمِ النماذج والعمليات الحساسة من هجمات تزوير الطلبات عبر المواقع CSRF باستخدام random_bytes والمقارنة الثابتة زمنياً hash_equals.
أقسام الدرس
6
الأمثلة
6
اللغة
العربية
تاريخ النشر
غير محدد
المشاهدات
0
محتوى الدرس
في هذا الدرس 6تصفّح المواضيع

تعتمد هجمات تزوير الطلبات عبر المواقع (CSRF) على خداع متصفح المستخدم المسجل دخوله لإرسال طلب تغييري غير مقصود (كتغيير البريد أو تحويل رصيد) إلى تطبيق PHP الخاص بك.

  • ترفق المتصفحات كوكيز الجلسة (PHPSESSID) تلقائياً مع الطلبات المرسلة للموقع ما لم تُضبط طبقات الحماية.
  • بدون توكن مزامنة سري مرتبط بالجلسة، لا يستطيع خادم PHP التفرقة بين نموذج أرسله المستخدم بإرادته ونموذج مخفي أُرسل من موقع خارجي.
  • لا تنفذ أبداً أي عمليات تغيير بيانات (إضافة أو تعديل أو حذف أو خروج) عبر طلبات GET.

يُعد فرض طريقة POST الشرط الأول للحماية من CSRF، لأن روابط GET يمكن تشغيلها بمجرد وضعها داخل وسم صورة <img src="...">.

مخطط توضيحي: محاكي التحقق من توكن الحماية CSRF في PHP
PHP
<?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
<?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
<?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() المقارنة الآمنة في زمن ثابت.

مخطط تدفق التنفيذ العملي لقسم المقارنة الآمنة ضد هجمات التوقيت بـ hash_equals في لغة PHP
PHP
<?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
<?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 Generationbin2hex(random_bytes(32))Token guessing & brute-forcing
Timing-Safe Comparisonhash_equals($sess, $post)Timing side-channel leaks
Token TTL & Rotation$_SESSION["csrf_time"]Stale or replayed tokens
Cookie Policysamesite => "Lax"Cross-site cookie attachment on POST

PHP
<?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

راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.

0 / 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

راجع المعنى، اشرحه لنفسك، ثم علّم البطاقة كمفهومة.

0 / 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 ويرسلها إلى متصفح العميل بعد التنفيذ.
للمراجعة

اقرأ البطاقات بحرية. سجّل الدخول لحفظ تقدم مراجعتك.

من الفهم للتطبيق

مهامك قبل الاختبار

خطوة صغيرة كل مرة. وإنجاز تقدر ترجع له.

0/2مهام منجزة
  1. أنشئ ملف PHP محلياً ونفّذ المثال البرمجي المشروح في قسم كيف تستغل هجمات CSRF الجلسات النشطة للتحقق من المخرجات.

  2. بدّل بين الحالات الثلاث في المحاكي التفاعلي ضمن قسم توليد توكنات تشفيرية آمنة بـ random_bytes وقارن مخرجات التنفيذ.

تأشير المهمة إقرار منك بإتمامها، وليس تصحيحًا آليًا للكود. يُحفظ تقدمك في حسابك.

مساحة التحدي

اختبر فهمك.
ووصلها للإتقان.

كل سؤال خطوة لقدّام. اختار، اكتب كود، وشوف نتيجتك فورًا.

8أسئلة 1أنواع تحديات 100%هدف الإتقان
تصحيح تلقائي. فرص بلا حدود.

أكمل المهام، واجمع كل الإجابات الصحيحة. أقل من 100%؟ راجع وأعد المحاولة.

الأسئلة متاحة للاطلاع. سجّل الدخول للتفاعل وحفظ تقدمك.

لمحة عن التحدي8 أسئلة تنتظرك
01

لماذا يمكن أن ينجح هجوم CSRF إذا كان ملف معالجة النموذج في PHP يفحص $_SESSION["user_id"] فقط؟

تثبت كوكيز الجلسة هوية المتصفح لكنها لا تثبت أن المستخدم قصد إرسال ذلك النموذج تحديداً.

02

أي تعبير في PHP يُوصى به لتوليد توكن CSRF عشوائي آمن تشفيرياً بطول 64 محرفاً؟

تولد random_bytes(32) بايتات عشوائية تشفيرية وتحولها bin2hex() إلى 64 محرفاً سداسياً عشرياً.

03

أين يجب تخزين النسخة المرجعية لتوكن المزامنة CSRF على جانب الخادم؟

حفظ التوكن في $_SESSION يربطه بجلسة المستخدم الحالية على الخادم.

04

لماذا يجب استخدام دالة hash_equals($sessionToken, $postToken) بدلاً من === عند التحقق من توكن CSRF؟

تستغرق المقارنة نفس الزمن بغض النظر عن موقع أول حرف مختلف.

05

كيف يتم تمرير توكن CSRF عادةً داخل نموذج HTML يرسل بطريقة POST؟

يرسل الحقل المخفي قيمة التوكن ضمن جسم طلب POST دون إزعاج المستخدم.

06

لماذا يجب عدم تنفيذ العمليات المغيرة للبيانات (مثل حذف سجل) عبر طلبات HTTP GET مطلقاً؟

يجب أن تكون طلبات GET للقراءة فقط دون إحداث تغييرات جانبية.

07

ماذا يحدث في PHP 8 إذا مررت القيمة null أو مصفوفة كمعامل ثانٍ لدالة hash_equals()؟

يجب دائماً التحقق بـ is_string() أو التحويل النصي قبل استدعاء الدالة.

08

أي خاصية في الكوكيز توفر طبقة دفاع إضافية من جهة المتصفح ضد إرسال النماذج العابرة للمواقع؟

تمنع SameSite=Lax إرسال الكوكيز مع طلبات POST العابرة للمواقع.