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

تشفير كلمات المرور والتحقق منها عبر password_hash و password_verify في PHPPHP Password Hashing and Verification

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

صُممت دوال التجزئة العامة مثل md5() وsha1() وhash("sha256", ...) لتكون فائقة السرعة، مما يجعلها كارثية وغير صالحة إطلاقاً لتخزين كلمات مرور المستخدمين.

  • تستطيع البطاقات الرسومية الحديثة حساب مليارات التجزئات من نوع MD5 أو SHA-256 في الثانية الواحدة، مما يكسر كلمات المرور خلال ثوانٍ.
  • تُنتج كلمات المرور المتطابقة نفس بصمة MD5 أو SHA-256 عند غياب الملح الفريد، مما يجعلها عرضة لجداول القيم المحسوبة مسبقاً (Rainbow Tables).
  • يتطلب تخزين كلمات المرور خوارزميات بطيئة عمداً وقابلة لزيادة عامل التكلفة مع توليد ملح تشفيري عشوائي فريد لكل كلمة مرور.

بينما تنتهي دالة hash("sha256", ...) في أجزاء من النانو ثانية بدون ملح، تطبق دالة password_hash() عامل تكلفة متكيفاً وتدمج ملحاً عشوائياً فريداً داخل النص الناتج.

مخطط توضيحي: محاكي تجزئة كلمات المرور والتحقق منها في PHP 8.4
PHP
<?php
$plain = "Summer2026!";

// INSECURE: Fast deterministic digest (same input = same output)
$fastSha = hash('sha256', $plain);

// SECURE: Slow adaptive hash with unique random salt
$secureHash = password_hash($plain, PASSWORD_DEFAULT);

echo "SHA-256 len : " . strlen($fastSha) . "\n";
echo "Prefix      : " . substr($secureHash, 0, 7) . "\n";

تُولّد الدالة المدمجة password_hash() نصاً واحداً متكاملاً يضم معرف الخوارزمية، ومعاملات التكلفة، والملح العشوائي الفريد، وخلاصة التجزئة النهائية.

  • استخدم الثابت PASSWORD_DEFAULT ليتبنى تطبيقك تلقائياً الخوارزميات الأقوى المعتمدة في إصدارات PHP المستقبلية.
  • استدعاء password_hash() مرتين لنفس كلمة المرور تماماً يُنتج نصين مختلفين كلياً لأن كل استدعاء يولد ملحاً عشوائياً جديداً.
  • عرّف عمود كلمة المرور في قاعدة البيانات دائماً بنوع VARCHAR(255) لضمان عدم اقتطاع التجزئات الأطول مستقبلاً.

رغم تطابق قيمة $password، فإن مقارنة $hashOne === $hashTwo ترجع No لأن كل استدعاء يدمج ملحاً عشوائياً مختلفاً.

PHP
<?php
$password = "MyVault#9876";

$hashOne = password_hash($password, PASSWORD_DEFAULT);
$hashTwo = password_hash($password, PASSWORD_DEFAULT);

$info = password_get_info($hashOne);
echo "Algo Name  : " . $info['algoName'] . "\n";
echo "Same Hash? : " . ($hashOne === $hashTwo ? "Yes" : "No") . "\n";

يدعم PHP كلاً من خوارزمية PASSWORD_BCRYPT المعتمدة على تكلفة المعالج، وخوارزمية PASSWORD_ARGON2ID التي تضيف مقاومة الذاكرة ضد أجهزة الكسر المتخصصة.

  • مع Bcrypt مرر الخيار ["cost" => 12] (رفعت نسخة PHP 8.4 التكلفة الافتراضية لخوارزمية Bcrypt من 10 إلى 12).
  • مع Argon2id اضبط خيارات memory_cost وtime_cost وthreads لمقاومة هجمات البطاقات الرسومية وشرائح ASIC.
  • انتبه إلى أن خوارزمية Bcrypt تقتطع المدخلات التي تتجاوز 72 بايت، بينما تعالج Argon2id كامل طول النص المدخل.

تُضمّن كلتا الخوارزميتين إعدادات التكلفة والذاكرة مباشرة داخل النص الناتج، فلا تحتاج عملية التحقق لأي إعدادات خارجية.

PHP
<?php
$secret = "EnterpriseKey$2026";

$bcrypt = password_hash($secret, PASSWORD_BCRYPT, ['cost' => 12]);
$argon = password_hash($secret, PASSWORD_ARGON2ID, [
    'memory_cost' => 65536,
    'time_cost'   => 4,
    'threads'     => 1,
]);

echo "Bcrypt : " . substr($bcrypt, 0, 7) . "\n";
echo "Argon2 : " . substr($argon, 0, 19) . "\n";

لأن كل تجزئة تحتوي على ملح مختلف، لا يمكنك أبداً التحقق من كلمة المرور بمقارنة النصوص بالمعامل ===، بل يجب دائماً استدعاء الدالة password_verify($plainPassword, $storedHash).

  • تستخرج دالة password_verify() الخوارزمية والتكلفة والملح من $storedHash تلقائياً، وتجري المقارنة في زمن ثابت.
  • تحمي المقارنة ذات الزمن الثابت من هجمات التوقيت التي تقيس فروق الاستجابة بالميكروثانية لاستنتاج صحة الأحرف.
  • إذا لم يُعثر على البريد الإلكتروني في قاعدة البيانات، نفّذ password_verify() على تجزئة وهمية لمنع كشف وجود الحسابات عبر فرق الوقت.

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

مخطط تدفق التنفيذ العملي لقسم التحقق عند تسجيل الدخول بـ password_verify في لغة PHP
PHP
<?php
$storedHash = password_hash("CorrectHorse99!", PASSWORD_DEFAULT);

$okLogin = password_verify("CorrectHorse99!", $storedHash);
$badLogin = password_verify("WrongGuess123", $storedHash);

echo "Valid password   : " . ($okLogin ? "MATCH" : "FAIL") . "\n";
echo "Invalid password : " . ($badLogin ? "MATCH" : "FAIL") . "\n";

فور نجاح التحقق عبر password_verify() أثناء تسجيل الدخول، استدعِ دالة password_needs_rehash() لفحص ما إذا كانت التجزئة المخزنة تستخدم تكلفة قديمة تحتاج للترقية.

  • تُعد لحظة تسجيل الدخول الناجح الفرصة الوحيدة التي يمتلك فيها الخادم كلمة المرور الصحيحة مؤقتاً في الذاكرة.
  • إذا أرجعت password_needs_rehash($storedHash, PASSWORD_DEFAULT, ["cost" => 12]) القيمة true، ولّد تجزئة جديدة وحدّث سجل المستخدم بـ PDO.
  • تتيح هذه الآلية ترقية تشفير حسابات المستخدمين تدريجياً دون إجبارهم على إعادة تعيين كلمات المرور.

لأن $legacyHash أُنشئت بتكلفة 10، تكتشف دالة password_needs_rehash() اختلافها عن التكلفة المطلوبة 12 وتنفذ الترقية فوراً.

PHP
<?php
$legacyHash = password_hash("Pass#2026", PASSWORD_BCRYPT, ['cost' => 10]);
$input = "Pass#2026";

if (password_verify($input, $legacyHash)) {
    if (password_needs_rehash($legacyHash, PASSWORD_BCRYPT, ['cost' => 12])) {
        $newHash = password_hash($input, PASSWORD_BCRYPT, ['cost' => 12]);
        echo "Upgraded to cost 12: " . substr($newHash, 0, 7) . "\n";
    }
}

التزم بقائمة التحقق الأمنية الشاملة لدورة حياة كلمات المرور في PHP 8.4 أثناء التسجيل والدخول وتصميم الجداول.

  • شفر كلمات المرور حصرياً عبر password_hash() باستخدام PASSWORD_DEFAULT أو PASSWORD_ARGON2ID.
  • تحقق من الدخول حصرياً عبر password_verify() ورقِّ التجزئات القديمة عبر password_needs_rehash().
  • افرض حداً أدنى لطول كلمة المرور (من 8 إلى 12 محرفاً) دون حذف الرموز الخاصة.

تجمع الدالة المساعدة بين التحقق الآمن والترقية التلقائية للتشفير في وحدة برمجية واحدة قابلة لإعادة الاستخدام.

الدالة / الثابتالوظيفة الأساسيةالاستخدام الموصى به
password_hash()Create salted adaptive hashRegistration & password change
password_verify()Timing-safe hash verificationEvery login authentication check
password_needs_rehash()Detect outdated cost or algorithmImmediately after successful login
PASSWORD_DEFAULTCurrent strongest default (Bcrypt 12)Standard portable PHP deployments
PASSWORD_ARGON2IDMemory-hard + CPU-hard hashingHigh-security PHP 8.4 servers

PHP
<?php
function verifyAndUpgrade(string $plain, string $hash): ?string {
    if (!password_verify($plain, $hash)) {
        return null;
    }
    if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
        return password_hash($plain, PASSWORD_DEFAULT);
    }
    return $hash;
}
echo (verifyAndUpgrade("Admin!234", password_hash("Admin!234", PASSWORD_DEFAULT)) !== null)
    ? "Verified OK\n" : "Rejected\n";
خلاصة المعرفة ومحطة الانتقال

ملخص الدرس

حصاد الدرس

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

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

التزم بقائمة التحقق الأمنية الشاملة لدورة حياة كلمات المرور في PHP 8.4 أثناء التسجيل والدخول وتصميم الجداول. شفر كلمات المرور حصرياً عبر password_hash() باستخدام PASSWORD_DEFAULT أو PASSWORD_ARGON2ID. تحقق من الدخول حصرياً عبر password_verify() ورقِّ التجزئات القديمة عبر password_needs_rehash(). افرض حداً أدنى لطول كلمة المرور (من 8 إلى 12 محرفاً) دون حذف الرموز الخاصة.

مشاركة

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

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

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

المصطلحات4

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

0 / 4 مفهومةأول بطاقة، أول خطوة
01 Password Hashing تشفير كلمات المرور
خزّن كلمات مرور المستخدمين بأمان تام في PHP 8 باستخدام password_hash وخوارزميات Bcrypt وArgon2id ودالة التحقق password_verify.
للمراجعة
02 Why MD5 and SHA256 Fail for Passwords لماذا تفشل MD5 و SHA256 في حماية كلمات المرور
المفهوم الأساسي لقسم لماذا تفشل MD5 و SHA256 في حماية كلمات المرور في لغة PHP الحديثة.
للمراجعة
03 Hashing Passwords with password_hash تجزئة كلمات المرور بدالة password_hash
الصياغة البرمجية والتطبيق العملي المباشر لقسم تجزئة كلمات المرور بدالة password_hash.
للمراجعة
04 Bcrypt vs Argon2id Algorithms مقارنة خوارزمية Bcrypt وخوارزمية Argon2id
قواعد التحقق وأفضل الممارسات القياسية الخاصة بقسم مقارنة خوارزمية Bcrypt وخوارزمية Argon2id.
للمراجعة

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

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

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

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

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

مساحة التحدي

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

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

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

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

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

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

لماذا تُعد دالتا md5() و hash("sha256", ...) غير آمنتين لتخزين كلمات مرور المستخدمين؟

تسمح التجزئة السريعة للبطاقات الرسومية بتجربة مليارات التخمينات في الثانية.

02

ماذا تضمّن دالة password_hash($pass, PASSWORD_DEFAULT) داخل النص الناتج؟

تُدمج جميع البيانات اللازمة لدالة password_verify() داخل نص واحد متكامل.

03

ما نوع العمود الموصى به في قاعدة البيانات لتخزين مخرجات دالة password_hash()؟

يتسع VARCHAR(255) لتجزئات Bcrypt وArgon2id وأي خوارزميات مستقبلية.

04

لماذا يُرجع استدعاء password_hash("secret", PASSWORD_DEFAULT) مرتين نصين مختلفين تماماً؟

يتم توليد ملح عشوائي جديد في كل استدعاء.

05

ما الدالة التي يجب استخدامها للتحقق من كلمة مرور الدخول مقابل التجزئة المخزنة؟

تستخرج password_verify() الملح والتكلفة المدمجين وتقارن في زمن ثابت.

06

متى يكون الوقت الصحيح لاستدعاء دالة password_needs_rehash()؟

لحظة الدخول الناجح هي الوقت الذي تتوفر فيه كلمة المرور الصحيحة بالذاكرة لتوليد التجزئة المحدثة.

07

ما الميزة الأمنية التي تقدمها خوارزمية PASSWORD_ARGON2ID مقارنة بـ PASSWORD_BCRYPT؟

تتيح Argon2id ضبط تكلفة الذاكرة والوقت والمسارات دون حد 72 بايت الخاص بـ Bcrypt.

08

ما هي قيمة التكلفة الافتراضية (cost) لخوارزمية Bcrypt في PHP 8.4 عند عدم تمرير مصفوفة خيارات؟

رفعت نسخة PHP 8.4 التكلفة الافتراضية لخوارزمية Bcrypt من 10 إلى 12.