إنشاء مفتاح مرور لتسجيل الدخول بدون كلمة مرور

تجعل مفاتيح المرور حسابات المستخدمين أكثر أمانًا وبساطة وسهولة في الاستخدام.

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

تتم مزامنة مفاتيح المرور على جميع الأجهزة باستخدام مقدّمي مفاتيح المرور، مثل "مدير كلمات المرور في Google" وسلسلة مفاتيح iCloud.

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

آلية إنشاء مفتاح المرور

قبل أن يتمكّن المستخدم من تسجيل الدخول باستخدام مفتاح مرور، عليك إنشاء مفتاح المرور، وربطه بحساب مستخدم، وتخزين مفتاحه العام على خادمك.

يمكنك مطالبة المستخدمين بإنشاء مفتاح مرور في إحدى الحالتَين التاليتَين:

  • أثناء الاشتراك أو بعده
  • بعد تسجيل الدخول
  • بعد تسجيل الدخول باستخدام مفتاح مرور من جهاز آخر (أي أنّ [authenticatorAttachment](https://web.dev/articles/passkey-form-autofill#authenticator-attachment) هو cross-platform).
  • في صفحة مخصّصة يمكن للمستخدمين من خلالها إدارة مفاتيح المرور

لإنشاء مفتاح مرور، يمكنك استخدام WebAuthn API.

تتألف خطوات تسجيل مفتاح المرور من أربعة مكوّنات:

  • الجانب الخلفي: يخزّن تفاصيل حساب المستخدم، بما في ذلك المفتاح العام.
  • الواجهة الأمامية: تتواصل مع المتصفّح وتسترجع البيانات اللازمة من الخلفية.
  • المتصفّح: يشغّل JavaScript ويتفاعل مع WebAuthn API.
  • مقدّم مفتاح المرور: ينشئ مفتاح المرور ويخزّنه. ويكون هذا عادةً مدير كلمات مرور، مثل "مدير كلمات المرور في Google"، أو مفتاح أمان.
عملية إنشاء مفتاح مرور وتسجيله
عملية إنشاء مفتاح مرور وتسجيله

قبل إنشاء مفتاح مرور، تأكَّد من أنّ النظام يستوفي المتطلبات الأساسية التالية:

  • يتم إثبات ملكية حساب المستخدم من خلال طريقة آمنة (مثل البريد الإلكتروني أو إثبات الملكية عبر الهاتف أو ربط الهوية) خلال فترة قصيرة ومفيدة.

  • يمكن للواجهة الأمامية والخلفية التواصل بأمان لتبادل بيانات المستندات المُعتمَدة.

  • أن يكون المتصفّح متوافقًا مع WebAuthn وإنشاء مفاتيح المرور

يمكننا أن نعرض لك كيفية التحقّق من معظمها في الأقسام التالية.

بعد استيفاء النظام لهذه الشروط، تحدث العملية التالية لإنشاء مفتاح مرور:

  1. يشغِّل النظام عملية إنشاء مفتاح المرور عندما يُبدِئ المستخدم الإجراء (على سبيل المثال، النقر على زر "إنشاء مفتاح مرور" في صفحة إدارة مفاتيح المرور أو بعد الانتهاء من التسجيل).
  2. تطلب الواجهة الأمامية بيانات الاعتماد اللازمة من الخلفية، بما في ذلك معلومات المستخدم والتحدي ومعرّفات بيانات الاعتماد لمنع تكرار المحتوى.
  3. تُطلِق الواجهة الأمامية navigator.credentials.create() لطلب مقدّم مفتاح المرور للجهاز إنشاء مفتاح مرور باستخدام المعلومات الواردة من الخلفية. يُرجى العِلم أنّ هذه الدعوة تُرجع وعدًا.
  4. يُجري جهاز المستخدم مصادقة له باستخدام طريقة مقاييس حيوية أو رقم تعريف شخصي أو نمط لإنشاء مفتاح المرور.
  5. ينشئ مقدّم مفتاح المرور مفتاح مرور ويعرض مفتاحًا عامًا مصادقة للواجهة الأمامية، ما يؤدي إلى حلّ المشكلة.
  6. تُرسِل الواجهة الأمامية بيانات اعتماد المفتاح العام الذي تم إنشاؤه إلى الواجهة الخلفية.
  7. تخزِّن الخلفية المفتاح العام والبيانات المهمة الأخرى للمصادقة في المستقبل.
  8. تُرسِل الخلفية إشعارًا إلى المستخدم (على سبيل المثال، باستخدام البريد الإلكتروني) لتأكيد إنشاء مفتاح المرور ورصد أي وصول غير مصرَّح به محتمل.

تضمن هذه العملية عملية تسجيل مفاتيح المرور الآمنة والسلسة للمستخدمين.

التوافق

تتوافق معظم المتصفحات مع WebAuthn، مع بعض الثغرات البسيطة. يُرجى الاطّلاع على passkeys.dev للحصول على تفاصيل التوافق مع المتصفّح ونظام التشغيل.

إنشاء مفتاح مرور جديد

لإنشاء مفتاح مرور جديد، إليك العملية التي يجب أن تتّبعها الواجهة الأمامية:

  1. التحقّق من التوافق:
  2. استرداد المعلومات من الواجهة الخلفية:
  3. استدعاء WebAuth API لإنشاء مفتاح مرور
  4. أرسِل المفتاح العام الذي تم إرجاعه إلى الخلفية.
  5. احفظ بيانات الاعتماد.

توضّح الأقسام التالية كيفية إجراء ذلك.

التحقّق من التوافق

قبل عرض زر "إنشاء مفتاح مرور جديد"، يجب أن تتحقّق الواجهة الأمامية مما يلي:

  • يتوافق المتصفّح مع WebAuthn باستخدام PublicKeyCredential.

Browser Support

  • Chrome: 67.
  • Edge: 18.
  • Firefox: 60.
  • Safari: 13.

Source

  • يتيح الجهاز مصادقة المنصة (يمكنه إنشاء مفتاح مرور و المصادقة باستخدام مفتاح المرور) باستخدام PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable().

Browser Support

  • Chrome: 67.
  • Edge: 18.
  • Firefox: 60.
  • Safari: 13.

Source

Browser Support

  • Chrome: 108.
  • Edge: 108.
  • Firefox: 119.
  • Safari: 16.

Source

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

// Availability of `window.PublicKeyCredential` means WebAuthn is usable.  
// `isUserVerifyingPlatformAuthenticatorAvailable` means the feature detection is usable.  
// `isConditionalMediationAvailable` means the feature detection is usable.  
if (window.PublicKeyCredential &&  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&  
    PublicKeyCredential.isConditionalMediationAvailable) {  
  // Check if user verifying platform authenticator is available.  
  Promise.all([  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),  
    PublicKeyCredential.isConditionalMediationAvailable(),  
  ]).then(results => {  
    if (results.every(r => r === true)) {  
      // Display "Create a new passkey" button  
    }  
  });  
}  

في هذا المثال، يجب عدم عرض الزر إنشاء مفتاح مرور جديد إلا عند استيفاء كل الشروط.

استرجاع المعلومات من الواجهة الخلفية

عندما ينقر المستخدم على الزر، احصل على المعلومات المطلوبة من الخلفية للاتصال برقم navigator.credentials.create().

يعرض مقتطف الرمز التالي عنصر JSON يحتوي على المعلومات المطلوبة لمحاولة استدعاء navigator.credentials.create():

// Example `PublicKeyCredentialCreationOptions` contents
{
  challenge: *****,
  rp: {
    name: "Example",
    id: "example.com",
  },
  user: {
    id: *****,
    name: "john78",
    displayName: "John",
  },
  pubKeyCredParams: [{
    alg: -7, type: "public-key"
  },{
    alg: -257, type: "public-key"
  }],
  excludeCredentials: [{
    id: *****,
    type: 'public-key',
    transports: ['internal'],
  }],
  authenticatorSelection: {
    authenticatorAttachment: "platform",
    requireResidentKey: true,
  }
}

تحتوي أزواج المفتاح والقيمة في العنصر على المعلومات التالية:

  • challenge: تحدّي ينشئه الخادم في ArrayBuffer لهذا التسجيل.
  • rp.id: يمكن أن يحدِّد رقم تعريف الطرف الموثوق به (RP ID) والنطاق والموقع الإلكتروني إما النطاق أو اللاحقة القابلة للتسجيل. على سبيل المثال، إذا كان مصدر موفِّر المحتوى هو https://login.example.com:1337، يمكن أن يكون رقم تعريف موفِّر المحتوىlogin.example.com أو example.com. إذا تم تحديد رقم تعريف موفِّر خدمات البريد الإلكتروني على أنّه example.com، يمكن للمستخدم المصادقة على login.example.com أو على أيّ نطاقات فرعية على example.com. اطّلِع على مقالة السماح بإعادة استخدام مفتاح المرور على جميع مواقعك الإلكترونية باستخدام طلبات المصدر ذات الصلة للحصول على مزيد من المعلومات حول هذا الموضوع.
  • rp.name: اسم الجهة الموثوق بها تم إيقاف هذا الإجراء نهائيًا في WebAuthn L3، ولكن تم تضمينه لأسباب تتعلق بالتوافق.
  • user.id: رقم تعريف مستخدم فريد في ArrayBuffer، يتم إنشاؤه عند إنشاء الحساب. يجب أن يكون دائمًا، على عكس اسم المستخدم الذي يمكن تعديله. يحدِّد معرّف المستخدم حسابًا، ولكن يجب ألا يحتوي على أي معلومات تحديد الهوية الشخصية (PII). من المحتمل أن يكون لديك معرّف مستخدم في نظامك، ولكن إذا لزم الأمر، يمكنك إنشاء معرّف مخصّص لمفاتيح المرور للحفاظ على خلوه من أي معلومات تحديد هوية شخصية.
  • user.name: معرّف فريد للحساب يمكن للمستخدم التعرّف عليه، مثل عنوان بريده الإلكتروني أو اسمه المستخدِم. سيتم عرض هذا في أداة اختيار الحسابات.
  • user.displayName: اسم مطلوب للحساب وأكثر ملاءمةً للمستخدم ولا يُشترط أن يكون فريدًا، ويمكن أن يكون الاسم الذي اختاره المستخدم. إذا لم يكن لديك قيمة مناسبة لإدراجها هنا، يمكنك إدخال سلسلة فارغة. قد يظهر هذا الخيار في أداة اختيار الحساب استنادًا إلى المتصفّح.
  • pubKeyCredParams: تُحدِّد خوارزميات المفتاح العام المتوافقة مع RP (الطرف الموثوق به). ننصح بضبطه على [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]. يحدِّد هذا الإجراء مدى توفُّر ECDSA مع P-256 وRSA PKCS#1 ، ويمنح توفُّر هذه العناصر تغطية كاملة.
  • excludeCredentials: قائمة بأرقام تعريف بيانات الاعتماد المسجَّلة منع تسجيل الجهاز نفسه مرتين من خلال توفير قائمة بأرقام تعريف بيانات الاعتماد المسجّلة يجب أن يحتوي العنصر transports member، في حال توفّره، على نتيجة استدعاء العنصر getTransports() أثناء تسجيل كل بيانات اعتماد.
  • authenticatorSelection.authenticatorAttachment: اضبط هذا الخيار على "platform" مع hint: ['client-device'] إذا كان إنشاء مفتاح المرور هذا ترقية من كلمة مرور، على سبيل المثال في عرض ترويجي بعد تسجيل الدخول. يشير الرمز "platform" إلى أنّ مقدّم طلب المصادقة يريد ملفًا شخصيًا مصادقًا (ملف مصادقة مضمّن في جهاز النظام الأساسي) لا يطلب مثلاً إدخال مفتاح أمان USB. يتوفّر للمستخدم خيار أسهل لإنشاء مفتاح مرور.
  • authenticatorSelection.requireResidentKey: اضبطها على قيمة منطقية true. بيانات الاعتماد القابلة للاكتشاف (مفتاح مقيم): تخزِّن معلومات المستخدم في مفتاح المرور وتسمح للمستخدمين باختيار الحساب عند المصادقة.
  • authenticatorSelection.userVerification: يشير إلى ما إذا كان إثبات هوية المستخدم باستخدام قفل شاشة الجهاز هو "required" أو "preferred" أو "discouraged". الإعداد التلقائي هو "preferred"، ما يعني أنّ معتمِد الهوية قد يتخطّى عملية إثبات هوية المستخدم. اضبط هذا الخيار على "preferred" أو احذف السمة.

ننصحك بإنشاء العنصر على الخادم، وترميز ArrayBuffer باستخدام Base64URL، واسترداده من الواجهة الأمامية. بهذه الطريقة، يمكنك فك ترميز ملف التحميل باستخدام PublicKeyCredential.parseCreationOptionsFromJSON() ونقله مباشرةً إلى navigator.credentials.create().

يوضّح مقتطف الرمز البرمجي التالي كيفية جلب المعلومات اللازمة لإنشاء مفتاح المرور وفك ترميزها.

// Fetch an encoded `PubicKeyCredentialCreationOptions` from the server.
const _options = await fetch('/webauthn/registerRequest');

// Deserialize and decode the `PublicKeyCredentialCreationOptions`.
const decoded_options = JSON.parse(_options);
const options = PublicKeyCredential.parseCreationOptionsFromJSON(decoded_options);
...

طلب WebAuthn API لإنشاء مفتاح مرور

يُرجى الاتصال على navigator.credentials.create() لإنشاء مفتاح مرور جديد. تُعرِض واجهة برمجة التطبيقات وعدًا بالانتظار إلى أن يتفاعل المستخدم مع مربّع حوار مشروط.

Browser Support

  • Chrome: 60.
  • Edge: 18.
  • Firefox: 60.
  • Safari: 13.

Source

// Invoke WebAuthn to create a passkey.
const credential = await navigator.credentials.create({
  publicKey: options
});

إرسال بيانات اعتماد المفتاح العام المُسترَدة إلى الخلفية

بعد إثبات هوية المستخدم باستخدام قفل شاشة الجهاز، يتم إنشاء مفتاح مرور ويُحلّ الوعد من خلال عرض عنصر PublicKeyCredential في الواجهة الأمامية.

يمكن رفض الوعد لأسباب مختلفة. يمكنك معالجة هذه الأخطاء من خلال التحقّق من سمة name لعنصر Error:

  • InvalidStateError: سبق أن تم إنشاء مفتاح مرور على الجهاز. لن يتم عرض مربّع حوار خطأ للمستخدم. من المفترض ألا يتعامل الموقع الإلكتروني مع ذلك على أنّه خطأ. أراد المستخدم تسجيل الجهاز المحلي وتم تسجيله.
  • NotAllowedError: ألغى المستخدم العملية.
  • AbortError: تم إلغاء العملية.
  • الاستثناءات الأخرى: حدث خطأ غير متوقّع. يعرض المتصفّح مربّع حوار خطأ للمستخدم.

يحتوي عنصر بيانات اعتماد المفتاح العام على السمات التالية:

  • id: رقم تعريف مُشفَّر بترميز Base64URL لمفتاح المرور الذي تم إنشاؤه يساعد رقم التعريف هذا المتصفّح في تحديد ما إذا كان هناك مفتاح مرور مطابق في الجهاز عند المصادقة. يجب تخزين هذه القيمة في قاعدة البيانات في الخلفية.
  • rawId: إصدار ArrayBuffer من معرّف بيانات الاعتماد
  • response.clientDataJSON: بيانات العميل المشفَّرة بتنسيق ArrayBuffer
  • response.attestationObject: عنصر شهادة مُشفَّر بتنسيق ArrayBuffer يحتوي هذا الرمز على معلومات مهمة، مثل رقم تعريف مقدّم الطلبات والعلامات والمفتاح العام.
  • authenticatorAttachment: يعرض القيمة "platform" عند إنشاء بيانات الاعتماد هذه على جهاز مزوّد بمفتاح مرور.
  • type: يتم ضبط هذا الحقل دائمًا على "public-key".

رمزِّه باستخدام الطريقة .toJSON()، وسلسلِّه باستخدام JSON.stringify()، ثم أرسِله إلى الخادم.

...

// Encode and serialize the `PublicKeyCredential`.
const _result = credential.toJSON();
const result = JSON.stringify(_result);

// Encode and send the credential to the server for verification.  
const response = await fetch('/webauthn/registerResponse', {
  method: 'post',
  credentials: 'same-origin',
  body: result
});
...

حفظ بيانات الاعتماد

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

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

تتضمّن القائمة التالية السمات المقترَحة لحفظها:

  • معرّف بيانات الاعتماد: معرّف بيانات الاعتماد الذي تم إرجاعه مع بيانات اعتماد المفتاح العام.
  • اسم بيانات الاعتماد: اسم بيانات الاعتماد أدخِل اسمًا له يشير إلى مقدّم مفتاح المرور الذي تم إنشاؤه من خلاله، والذي يمكن التعرّف عليه استنادًا إلى AAGUID.
  • رقم تعريف المستخدم: رقم تعريف المستخدم المستخدَم لإنشاء مفتاح المرور.
  • المفتاح العام: المفتاح العام الذي تم إرجاعه مع بيانات اعتماد المفتاح العام هذا مطلوب للتحقّق من صحة تأكيد مفتاح المرور.
  • تاريخ الإنشاء والوقت: سجِّل تاريخ إنشاء مفتاح المرور ووقته. ويُعدّ ذلك مفيدًا لتحديد مفتاح المرور.
  • تاريخ ووقت آخر استخدام: يُسجِّل آخر تاريخ ووقت استخدم فيهما المستخدم مفتاح المرور لتسجيل الدخول. يكون ذلك مفيدًا لتحديد مفتاح المرور الذي استخدمه المستخدم (أو لم يستخدمه).
  • AAGUID: معرّف فريد لموفّر مفتاح المرور
  • علامة الأهلية للنسخ الاحتياطي: قيمة صحيحة إذا كان الجهاز مؤهلاً لمزامنة مفتاح المرور. تساعد هذه المعلومات المستخدمين في تحديد مفاتيح المرور القابلة للمزامنة ومفاتيح المرور المرتبطة بالجهاز (غير القابلة للمزامنة) في صفحة إدارة مفاتيح المرور.

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

إرسال إشارة في حال تعذُّر التسجيل

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

لمنع حدوث ذلك، يمكنك إرسال إشارة إلى مقدّم مفتاح المرور بأنّ مفتاح المرور غير معروف باستخدام واجهة برمجة التطبيقات Signal API. من خلال الاتصال بخدمة PublicKeyCredential.signalUnknownCredential() باستخدام معرّف مقدّم الخدمة و معرّف مستند الاعتماد، يمكن لمقدّم الخدمة إبلاغ موفّر مفتاح المرور بأنّه تمت إزالة مستند الاعتماد المحدّد أو أنّه غير متوفّر. يُرجى العِلم أنّ طريقة التعامل مع هذه الإشارة تعود إلى مقدّم مفتاح المرور، ولكن من المفترض أن تتم إزالة مفتاح المرور المرتبط إذا كان ذلك ممكنًا.

// Detect authentication failure due to lack of the credential
if (response.status === 404) {
  // Feature detection
  if (PublicKeyCredential.signalUnknownCredential) {
    await PublicKeyCredential.signalUnknownCredential({
      rpId: "example.com",
      credentialId: "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA" // base64url encoded credential ID
    });
  } else {
    // Encourage the user to delete the passkey from the password manager nevertheless.
    ...
  }
}

للاطّلاع على مزيد من المعلومات عن Signal API، يُرجى قراءة مقالة الحفاظ على تطابق مفاتيح المرور مع بيانات الاعتماد على خادمك باستخدام Signal API.

إرسال إشعار إلى المستخدم

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

قائمة التحقق

  • عليك إثبات هوية المستخدم (يُفضَّل استخدام البريد الإلكتروني أو طريقة آمنة) قبل السماح له بإنشاء مفتاح مرور.
  • يمكنك منع إنشاء مفاتيح مرور مكرّرة لموفّر مفاتيح المرور نفسه باستخدام excludeCredentials.
  • احفظ معرّف AAGUID لتحديد موفِّر مفتاح المرور واسم بيانات الاعتماد للمستخدم.
  • أرسِل إشارة إذا تعذّرت محاولة تسجيل مفتاح مرور باستخدام PublicKeyCredential.signalUnknownCredential().
  • أرسِل إشعارًا إلى المستخدم بعد إنشاء مفتاح مرور وتسجيله لحسابه.

الموارد

الخطوة التالية: تسجيل الدخول باستخدام مفتاح مرور من خلال الملء التلقائي للنموذج