طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 132 · تقنيات تطوير الواجهات الخلفية
الدرس 16 · الوحدة 7: التعامل مع حالة التطبيق والبيانات

تسجيل الدخول والخروج

بصمات كلمات المرور، والجلسات، وحماية الصفحات. ⏱ نحو 40 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تخزّن كلمات المرور ببصمات password_hash وتتحقق منها بـ password_verify.
  • تبني تدفق دخول وخروج كامل بالجلسات والتحويلات.
  • تحمي الصفحات بدالة مشتركة، وتميّز المصادقة من التفويض (401 و403).
  • تعرف أشهر مخاطر الدخول وحمايتها.

١لا تخزّن كلمة المرور أبدًا

أول قاعدة في الدخول: الخادم لا يحفظ كلمات المرور كما هي. فإن سُرقت قاعدة البيانات يومًا (وهذا يحدث لشركات كبرى) لن يجد المهاجم كلمات المرور نفسها. نحفظ بدلها بصمة Hash: ناتج دالة باتجاه واحد، يسهل حسابه ويستحيل عكسه عمليًّا.

<?php$hash = password_hash('Tvtc@2026', PASSWORD_DEFAULT);echo $hash . "\n";echo strlen($hash) . " حرفًا\n\n"; var_dump(password_verify('Tvtc@2026', $hash));   // الكلمة الصحيحةvar_dump(password_verify('tvtc@2026', $hash));   // حرف واحد مختلف

٢تدفق الدخول

  1. المستخدم يرسل اسمه وكلمة مروره بـ POST (وعلى HTTPS في الموقع الحقيقي).
  2. الخادم يجد المستخدم ويتحقق بـ password_verify.
  3. إن فشل: رمز 401 ورسالة عامة لا تكشف أيهما الخطأ، الاسم أم الكلمة.
  4. إن نجح: رقم جلسة جديد بـ session_regenerate_id، ثم يحفظ معلومات المستخدم (لا كلمة المرور) في $_SESSION.
  5. تحويل 303 إلى لوحة التحكم.
  6. كل صفحة محمية تبدأ بفحص الجلسة؛ ومن لا جلسة له يُحوَّل إلى صفحة الدخول.

لماذا رسالة عامة؟ لو قلنا «اسم المستخدم غير موجود» لعرف المهاجم أي الأسماء موجودة، وركّز تخمينه عليها.

٣النظام كاملًا

أربعة ملفات: auth.php وحدة الدخول المشتركة، وصفحة الدخول، ولوحة تحكم محمية، والخروج. تتبّع السجل: محاولة دخول للوحة بلا جلسة، ثم دخول بكلمة خاطئة، ثم صحيحة، ثم اللوحة، ثم الخروج، ثم محاولة اللوحة مرة أخرى:

<?phprequire __DIR__ . '/auth.php';$error = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST') {    $username = trim($_POST['username'] ?? '');    $password = $_POST['password'] ?? '';    $user = USERS[$username] ?? null;     if ($user && password_verify($password, $user['hash'])) {        session_regenerate_id(true);              // رقم جلسة جديد بعد الدخول        $_SESSION['user'] = ['username' => $username, 'name' => $user['name'], 'role' => $user['role']];        header('Location: dashboard.php', true, 303);        exit;    }    http_response_code(401);    $error = 'اسم المستخدم أو كلمة المرور غير صحيحة';}?><h2>تسجيل الدخول</h2><p style="color:red"><?= $error ?></p><form method="post">  <p><input name="username" placeholder="اسم المستخدم" value="saud"></p>  <p><input name="password" type="password" placeholder="كلمة المرور" value="Tvtc@2026"></p>  <button>دخول</button></form>
🧪 مختبر الخادماضغط ▶ تشغيل: ستُحوَّل إلى صفحة الدخول. سجّل الدخول (القيم معبأة)، ثم الخروج. جرّب نورة: noura و Noura#123 ، وجرّب كلمة خاطئة.

٤الصلاحيات: من يُسمح له بماذا؟

المصادقة Authentication: من أنت؟ (الدخول). التفويض Authorization: ماذا يُسمح لك؟ متدرب مسجّل دخوله لا يجوز أن يفتح صفحة إدارة المستخدمين. وهنا نرد بـ 403 Forbidden، لا 401:

الرمزالمعنىمثال
401 Unauthorizedلم تثبت هويتك (لم تسجل الدخول أو فشل الدخول)كلمة مرور خاطئة
403 Forbiddenعرفناك، لكن ليس لك صلاحيةمتدرب يفتح صفحة المدير
<?phpfunction require_role(string $role): array{    $u = require_login();    if ($u['role'] !== $role) {        http_response_code(403);        exit('ليست لديك صلاحية لهذه الصفحة');    }    return $u;} // في أعلى admin.php$u = require_role('admin');

٥مخاطر يجب أن تعرفها

الخطرالحماية
تخمين كلمات المرور آلاف المحاولاتحد للمحاولات: بعد 5 محاولات فاشلة انتظر دقائق.
سرقة رقم الجلسةHTTPS، و HttpOnly، و session_regenerate_id بعد الدخول.
كلمات مرور ضعيفةطول 8 على الأقل عند التسجيل.
CSRF: موقع آخر يرسل نموذجًا باسمكSameSite للكوكي، ورمز سري في النماذج المهمة (مقرر 243).

توقّع وتحقّق

الدالة التي تنشئ بصمة كلمة المرور للتخزين:

مع ملح عشوائي.

للتحقق من كلمة المرور عند الدخول:

البصمة تختلف كل مرة.

متدرب مسجّل يفتح صفحة المدير. الرمز:

عرفناه لكن لا صلاحية.

لماذا session_regenerate_id بعد الدخول؟

Session fixation.

الخلاصة

تحدٍّ برمجي: احمِ صفحة الإدارة

متقدم

نظام الدخول من الدرس جاهز. أكمل admin.php لتكون للمدير وحده، وأصلح ثغرة في login.php.

المطلوب:
  • زائر بلا جلسة يفتح admin.php ← تحويل 302 إلى login.php (استخدم require_login).
  • متدرب (noura) مسجّل يفتح admin.php ← 403 والنص ليست لديك صلاحية.
  • المدير (saud) ← 200 والصفحة كما هي.
  • في login.php ثغرة: تقارن كلمة المرور بـ == مع البصمة. استبدلها بـ password_verify.
👀 اعرض الاستجابة المطلوبة
✓ التحقق الآلي: يرسل المختبر هذه الطلبات إلى خادمك بالترتيب ثم يفحص الاستجابات
  1. GET /admin.php
  2. 👤 نورة POST /login.php (username=noura&password=Noura#123)
  3. 👤 نورة GET /admin.php
  4. 👤 سعود POST /login.php (username=saud&password=Tvtc@2026)
  5. 👤 سعود GET /admin.php
  6. 👤 مهاجم POST /login.php (username=saud&password=$2y$10$wVC2wSQ3Nh3geGIjvSF3b.nACPYbwVcXkrHbnncrkqQ32dFKNCLU6)
  • الكود يعمل بلا أخطاء أو تحذيرات
  • زائر ← 302
  • زائر ← يُحوَّل إلى login.php
  • نورة تدخل بنجاح ← 303
  • نورة تفتح admin.php ← 403
  • نورة ← ليست لديك صلاحية
  • سعود ← صفحة الإدارة
  • مهاجم يرسل البصمة نفسها كلمة مرور ← 401
  • تستخدم password_verify

💡 تلميح 1
ابدأ admin.php بـ $u = require_login(); ثم افحص $u['role'].
💡 تلميح 2
الثغرة: من سرق قاعدة البيانات يرسل البصمة نفسها فيدخل! password_verify تمنع ذلك.

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

<?phprequire __DIR__ . '/auth.php';$u = require_login();if ($u['role'] !== 'admin') {    http_response_code(403);    exit('ليست لديك صلاحية');}echo "<h2>إدارة المستخدمين</h2>";echo "<p>عدد المستخدمين: " . count(USERS) . "</p>";
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس