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

بناء نظام تسجيل الدخول والمصادقة وحماية الصفحات وتسجيل الخروج في PHPPHP Login Authentication Middleware Guards and Logout

نفّذ نظام تسجيل دخول آمن والتحقق من الهوية وتجديد الجلسة وحراس حماية الصفحات وميزة تذكرني وتسجيل الخروج النظيف في PHP 8.
أقسام الدرس
6
الأمثلة
6
اللغة
العربية
تاريخ النشر
غير محدد
المشاهدات
0
محتوى الدرس
في هذا الدرس 6تصفّح المواضيع

لمصادقة المستخدم، اجلب السجل المطابق للبريد الموحد عبر استعلام PDO محضر، ومرر التجزئة المخزنة إلى الدالة password_verify($inputPassword, $user["password_hash"]).

  • لا تضع كلمة المرور أبداً داخل شرط WHERE في SQL؛ بل اجلب السجل بالبريد أولاً ثم تحقق من التجزئة في PHP.
  • تستخرج دالة password_verify() الملح وخوارزمية التجزئة من النص المخزن تلقائياً.
  • افحص password_needs_rehash() بعد نجاح الدخول لترقية معامل التكلفة القديم تلقائياً.

ترجع password_verify() القيمة true عندما تتطابق كلمة المرور المدخلة مع التجزئة المخزنة.

مخطط توضيحي: محاكي تسجيل الدخول وتدوير الجلسة وحارس حماية الصفحات في PHP 8
PHP
<?php

$storedUser = [
    'id' => 42,
    'email' => '[email protected]',
    'password_hash' => password_hash('Correct#Pass9', PASSWORD_DEFAULT),
];

$inputPass = 'Correct#Pass9';
if ($storedUser && password_verify($inputPass, $storedUser['password_hash'])) {
    echo "Authenticated User ID: {$storedUser['id']}\n";
}

إذا أظهر نموذج الدخول رسالة "البريد غير موجود" منفصلة عن "كلمة المرور خاطئة"، سيتمكن المهاجم من اكتشاف عناوين البريد المسجلة؛ لذا أرجع دائماً رسالة خطأ موحدة للحالتين.

  • استخدم نفس الرسالة الموحدة: "Invalid email or password." سواء لم يوجد البريد أو فشلت دالة password_verify().
  • نفّذ فحص password_verify() وهمياً عند عدم وجود البريد لتوحيد زمن الاستجابة ومنع هجمات التوقيت.
  • قيّد عدد محاولات الدخول الفاشلة المتتالية لنفس العنوان أو البريد.

حتى عندما لا يوجد السجل، فإن استدعاء password_verify() مع تجزئة ثابتة يمنع كشف وجود الحساب عبر فرق زمن الاستجابة.

PHP
<?php

$dummyHash = '$2y$10$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
$userRow = false; // Email not found in DB
$inputPass = 'guess123';

$hashToCheck = $userRow ? $userRow['password_hash'] : $dummyHash;
$isValid = password_verify($inputPass, $hashToCheck) && $userRow !== false;

echo $isValid ? "Welcome\n" : "Invalid email or password.\n";

فور التحقق من صحة بيانات الدخول، استدعِ session_regenerate_id(true) قبل تخزين $_SESSION["user_id"] لإبطال أي محاولة لتثبيت الجلسة.

  • تمرير المعامل true يحذف ملف الجلسة القديم من الخادم ويصدر معرف جلسة جديداً تماماً.
  • خزّن فقط المعرفات الأساسية مثل user_id وuser_role ووقت الدخول في $_SESSION.
  • لا تخزن أبداً تجزئة كلمة المرور داخل مصفوفة $_SESSION.

تحفظ الجلسة معرف المستخدم الرقمي، وصلاحيته، والطابع الزمني لتسجيل الدخول.

PHP
<?php

$_SESSION = [];
$authenticatedId = 42;
$role = 'admin';

$_SESSION['user_id'] = $authenticatedId;
$_SESSION['user_role'] = $role;
$_SESSION['auth_time'] = 1762512000;

echo "Session bound to User #{$authenticatedId} ({$role})\n";

تفحص دالة حارس المصادقة (Auth Guard) وجود isset($_SESSION["user_id"]) في بداية كل صفحة محمية، وتحول الزوار غير المسجلين إلى /login فوراً.

  • اجمع الفحص في دالة موحدة قابلة لإعادة الاستخدام مثل requireAuth().
  • استدعِ دائماً exit; فور إرسال رأس التحويل Location: /login حتى لا يُنفذ الكود الخاص أسفله.
  • في صفحات الإدارة، أضف فحصاً ثانياً يتحقق من الصلاحية $_SESSION["user_role"] === "admin".

عند غياب user_id يمنع الحارس الوصول، وعند وجوده يسمح للمتحكم بالمتابعة.

مخطط تدفق التنفيذ العملي لقسم حماية الصفحات الخاصة بحارس المصادقة Auth Guard في لغة PHP
PHP
<?php

function checkAuthGuard(array $session): string
{
    if (empty($session['user_id'])) {
        return 'Redirect -> /login (401 Unauthorized)';
    }
    return 'Access Granted for User #' . $session['user_id'];
}

echo checkAuthGuard([]) . "\n";
echo checkAuthGuard(['user_id' => 42]) . "\n";

لا تخزن أبداً معرف المستخدم أو كلمة مروره في كوكي "تذكرني"؛ بل استخدم توكن مقسوماً إلى selector:validator بحيث يُخزن فقط ناتج تجزئة validator في قاعدة البيانات.

  • يُستخدم selector للبحث السريع المفهرس عن سجل التوكن في قاعدة البيانات.
  • تُجزّأ قيمة validator العشوائية السرية بـ hash("sha256", $validator) وتقارن عبر hash_equals().
  • حتى لو تسرب جدول التوكنات من قاعدة البيانات، لن يتمكن المهاجم من تزوير كوكي الدخول!

يجمع الكوكي بين selector:validator بينما تحفظ قاعدة البيانات فقط تجزئة SHA-256 للمفتاح السري.

PHP
<?php

$selector = bin2hex(random_bytes(8));
$validator = bin2hex(random_bytes(16));
$cookieValue = "$selector:$validator";
$dbHashedValidator = hash('sha256', $validator);

[$inSel, $inVal] = explode(':', $cookieValue, 2);
$valid = hash_equals($dbHashedValidator, hash('sha256', $inVal));
echo "Remember-Me Token Verified: " . ($valid ? 'YES' : 'NO') . "\n";

يجب أن يقبل معالج تسجيل الخروج طلبات POST المحمية بتوكن CSRF فقط، ويفرغ $_SESSION، ويلغي كوكي الجلسة في المتصفح، ثم يستدعي session_destroy().

  • لماذا نستخدم POST /logout؟ لأن جعل الخروج رابط GET يسمح لأي وسم صورة خبيث <img src="/logout"> بإخراج المستخدم قسرياً!
  • أفرغ مصفوفة $_SESSION = [] في الذاكرة قبل تدمير ملف الجلسة.
  • احذف توكن "تذكرني" من قاعدة البيانات ومن كوكيز المتصفح معاً عند الخروج.

تفريغ المصفوفة $_SESSION = [] يزيل جميع بيانات الجلسة فوراً من الذاكرة الحالية.

عملية المصادقةالإجراء الأمني الإلزاميالأهمية الأمنية
Login FailureUnified error message + dummy hash checkPrevents user email enumeration
Login Successsession_regenerate_id(true)Prevents session fixation attacks
Protected RouteAuth guard + exit; after redirectStops unauthorized execution immediately
Remember Me CookieSplit selector:validator (SHA-256 in DB)Prevents token theft from DB leaks
Logout EndpointPOST /logout + CSRF + clear cookiePrevents forced cross-site logout & replay

PHP
<?php

$_SESSION = ['user_id' => 42, 'user_role' => 'admin'];

// Step 1: Clear session array in memory
$_SESSION = [];

// Step 2: Verify memory is clean before session_destroy()
echo "Active Session Keys: " . count($_SESSION) . "\n";
echo "Logout complete -> Redirect to /login\n";
خلاصة المعرفة ومحطة الانتقال

ملخص الدرس

حصاد الدرس

مراجعة مركّزة لأهم المفاهيم والقواعد الهندسية التي أتقنتها في هذا الدرس قبل التطبيق العملي

ماذا تعلّمت في هذا الدرس؟

يجب أن يقبل معالج تسجيل الخروج طلبات POST المحمية بتوكن CSRF فقط، ويفرغ $_SESSION، ويلغي كوكي الجلسة في المتصفح، ثم يستدعي session_destroy(). لماذا نستخدم POST /logout؟ لأن جعل الخروج رابط GET يسمح لأي وسم صورة خبيث <img src="/logout"> بإخراج المستخدم قسرياً! أفرغ مصفوفة $_SESSION = [] في الذاكرة قبل تدمير ملف الجلسة. احذف توكن "تذكرني" من قاعدة البيانات ومن كوكيز المتصفح معاً عند الخروج.

مشاركة

هل كان هذا الدرس مفيدًا؟

جارٍ تحميل الآراء…

ابنِ لغتك البرمجية

المصطلحات4

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

0 / 4 مفهومةأول بطاقة، أول خطوة
01 Login & Auth System تسجيل الدخول والمصادقة
نفّذ نظام تسجيل دخول آمن والتحقق من الهوية وتجديد الجلسة وحراس حماية الصفحات وميزة تذكرني وتسجيل الخروج النظيف في PHP 8.
للمراجعة
02 Fetching User and Verifying Password Hash جلب المستخدم بالبريد والتحقق من التشفير
المفهوم الأساسي لقسم جلب المستخدم بالبريد والتحقق من التشفير في لغة PHP الحديثة.
للمراجعة
03 Preventing User Enumeration in Errors منع كشف وجود البريد عبر رسائل الخطأ الموحدة
الصياغة البرمجية والتطبيق العملي المباشر لقسم منع كشف وجود البريد عبر رسائل الخطأ الموحدة.
للمراجعة
04 Starting Authenticated Session Safely بدء جلسة المستخدم وتدوير المعرف فور الدخول
قواعد التحقق وأفضل الممارسات القياسية الخاصة بقسم بدء جلسة المستخدم وتدوير المعرف فور الدخول.
للمراجعة

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

حروف قليلة. معنى كامل.

الاختصارات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 محلياً ونفّذ المثال البرمجي المشروح في قسم جلب المستخدم بالبريد والتحقق من التشفير للتحقق من المخرجات.

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

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

مساحة التحدي

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

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

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

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

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

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

لماذا يجب أن يرجع تسجيل الدخول الفاشل رسالة موحدة "البريد أو كلمة المرور غير صحيحة" بدلاً من تحديد أيهما الخاطئ؟

الرسائل التفصيلية تكشف للمهاجم وجود الحساب من عدمه.

02

ما الدالة التي يجب استدعاؤها فور نجاح password_verify() أثناء تسجيل الدخول؟

تدوير معرف الجلسة عند تغير مستوى الصلاحية يمنع هجمات تثبيت الجلسة.

03

ماذا يحدث إذا نفّذ حارس المصادقة الأمر header("Location: /login"); دون استدعاء exit; بعده مباشرة؟

الدالة header() تضيف رأس HTTP فقط ولا توقف تنفيذ السكريبت تلقائياً!

04

في نمط توكن تذكرني المقسوم (selector:validator)، كيف يجب حفظ جزء الـ validator في قاعدة البيانات؟

حفظ تجزئة SHA-256 فقط يحمي جلسات المستخدمين حتى لو تسرب جدول التوكنات.

05

لماذا يجب أن يتم تسجيل الخروج عبر طلب POST محمي بتوكن CSRF بدلاً من رابط GET /logout عادي؟

العمليات التي تغير حالة الجلسة مثل الخروج يجب أن تتم عبر POST مع حماية CSRF.

06

كيف تفرغ جميع متغيرات الجلسة من الذاكرة أثناء تسجيل الخروج قبل استدعاء session_destroy()؟

إسناد $_SESSION = [] يفرغ جميع مفاتيح الجلسة في الذاكرة فوراً.

07

لماذا ننفذ password_verify() ضد تجزئة وهمية ثابتة حتى عندما لا يكون البريد الإلكتروني موجوداً في قاعدة البيانات؟

تستغرق Bcrypt نحو 100 مللي ثانية؛ وتخطيها عند غياب البريد يجعل الخادم يرد أسرع بـ 100 مرة مما يكشف غياب الحساب.

08

متى يجب استدعاء الدالة password_needs_rehash($storedHash, PASSWORD_DEFAULT)؟

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