Menu
CoddyTech

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

جزء من قسم المصادقة والأمان في رحلة Node.js على Coddy. الدرس 4 من 24.

يعيد الأشخاص استخدام كلمات المرور، لذلك قد تؤدي كلمة مرور مكشوفة بنص واضح إلى كشف حسابات في أماكن أخرى. خزّن تجزئة كلمة مرور مملّحة: قيمة مشتقة تتحقق منها بإعادة احتسابها، بدلًا من فك ترميزها.

const salt = randomBytes(16).toString('hex');
const key = await scryptAsync(inviteCode, salt, 32);
const saved = salt + '.' + key.toString('hex');

يجمع هذا المثال بين قيمة ملح ومفتاح مشتق لرمز دعوة. يتيح الفاصل استعادة قيمة الملح أثناء التحقق واشتقاق مفتاح آخر باستخدام المعلمات نفسها. تستخدم مهمة كلمة المرور الخاصة بك فاصلًا وطول مفتاح خاصين بها. أنشئ قيمة الملح لكل كلمة مرور جديدة، واشتق المفتاح، ثم خزّن كليهما. تظل التجزئات المملّحة بحاجة إلى كلمات مرور قوية لأن المهاجم يستطيع تجربة التخمينات.

challenge icon

التحدي

سهل

حاليًا، يحفظ التسجيل كل كلمة مرور تمامًا كما كُتبت. اجعله يحفظ تجزئة مملحة بدلًا من ذلك، حتى لا تكشف قاعدة البيانات المُسرَّبة كلمات المرور.

  1. في passwords.js، غيّر hashPassword(password) بحيث تُرجع النص الذي يحفظه التسجيل، بالصيغة: الملح، ثم نقطتان، ثم التجزئة:
    • الملح هو 16 بايتًا عشوائيًا، يُنشأ من جديد مع كل استدعاء ويُكتب بصيغة سداسية عشرية: 32 محرفًا من 0 إلى 9 ومن a إلى f.
    • التجزئة هي مفتاح بطول 64 بايتًا، تنشئه scrypt من كلمة المرور وذلك الملح، ويُكتب أيضًا بصيغة سداسية عشرية: 128 محرفًا. الوسيط الثالث لـ scryptAsync هو طول المفتاح بالبايت.
    • صِل بينهما بنقطتين. تبدو القيمة المحفوظة هكذا: f2bba0669d9fa2d203afc7e61bf8f065:c3f7cae352e023a8d4766da9a316b5a2c844100efff22c4079f89a8ab8512ac91c33227481c3afa88c3ba0e88347f9e3e0c22dad9d55f19688a350f71be8dbd2
  2. لا تحتاج إلى تغيير server.js. فالمسار POST /signup يستدعي hashPassword بالفعل ويحفظ ما تُرجعه. كما يتضمن GET /debug/users/:username، الذي يعرض ما حُفظ لمستخدم واحد. هذا المسار موجود فقط كي تتمكن من الاطلاع؛ لا يتضمنه تطبيق حقيقي أبدًا.

جرّب ذلك: اضغط «العب». أرسل POST /signup مع {"username": "ada", "password": "correct horse"}. ستحصل على الحالة 201 و{"username": "ada"}. أرسل الجسم نفسه مع "username": "bob". ثم أرسل GET /debug/users/ada، وبعده GET /debug/users/bob. تتكون كل قيمة passwordHash من 32 محرفًا، ثم نقطتين، ثم 128 محرفًا آخر. تختلف القيمتان، رغم أن ada وbob اختارا كلمة المرور نفسها.

جرّب بنفسك

import { randomBytes, scrypt } from 'node:crypto';
import { promisify } from 'node:util';

const scryptAsync = promisify(scrypt);

// يحوّل كلمة المرور إلى النص الذي تخزّنه signup.
// في الوقت الحالي، يحتفظ بكلمة المرور كما كُتبت.
export async function hashPassword(password) {
    return password;
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس المصادقة والأمان

تدرّب بنفسك: Playground لـ Express