یک رمز عبور برای ورود بدون رمز عبور ایجاد کنید

کلیدهای عبور، حساب‌های کاربری را ایمن‌تر، ساده‌تر و استفاده از آنها را آسان‌تر می‌کنند.

منتشر شده: ۱۲ اکتبر ۲۰۲۲، آخرین به‌روزرسانی: ۹ آوریل ۲۰۲۶

استفاده از کلیدهای عبور، امنیت را افزایش می‌دهد، ورود به سیستم را ساده می‌کند و جایگزین رمزهای عبور می‌شود. برخلاف رمزهای عبور معمولی که کاربران باید آنها را به خاطر بسپارند و به صورت دستی وارد کنند، کلیدهای عبور از مکانیسم‌های قفل صفحه نمایش دستگاه مانند بیومتریک یا پین استفاده می‌کنند و خطرات فیشینگ و سرقت اعتبار را کاهش می‌دهند.

کلیدهای عبور با استفاده از ارائه‌دهندگان کلید عبور مانند Google Password Manager و iCloud Keychain در بین دستگاه‌ها همگام‌سازی می‌شوند.

باید یک کلید عبور ایجاد شود و کلید خصوصی به همراه فراداده‌های لازم و کلید عمومی آن که برای احراز هویت در سرور شما ذخیره شده است، به طور ایمن در اختیار ارائه‌دهنده کلید عبور قرار گیرد. کلید خصوصی پس از تأیید کاربر در دامنه معتبر، امضا صادر می‌کند و کلیدهای عبور را در برابر فیشینگ مقاوم می‌سازد. کلید عمومی امضا را بدون ذخیره اعتبارنامه‌های حساس تأیید می‌کند و کلیدهای عبور را در برابر سرقت اعتبارنامه مقاوم می‌سازد.

نحوه ایجاد کلید عبور

قبل از اینکه کاربر بتواند با کلید عبور وارد سیستم شود، باید کلید عبور را ایجاد کنید، آن را به یک حساب کاربری مرتبط کنید و کلید عمومی آن را در سرور خود ذخیره کنید.

شما می‌توانید در یکی از موقعیت‌های زیر از کاربران بخواهید که یک رمز عبور ایجاد کنند:

  • در حین ثبت نام یا بعد از آن.
  • پس از ورود به سیستم.
  • پس از ورود به سیستم با استفاده از رمز عبور از دستگاه دیگر (یعنی، authenticatorAttachment cross-platform است).
  • در یک صفحه اختصاصی که کاربران می‌توانند کلیدهای عبور خود را مدیریت کنند.

برای ایجاد کلید عبور، از API وب‌اوثن (WebAuthn) استفاده می‌کنید.

چهار جزء جریان ثبت رمز عبور عبارتند از:

  • Backend : جزئیات حساب کاربری، از جمله کلید عمومی را ذخیره می‌کند.
  • فرانت‌اند (Frontend) : با مرورگر ارتباط برقرار می‌کند و داده‌های لازم را از بک‌اند دریافت می‌کند.
  • مرورگر : جاوا اسکریپت شما را اجرا می‌کند و با WebAuthn API تعامل دارد.
  • ارائه‌دهنده‌ی کلید عبور : کلید عبور را ایجاد و ذخیره می‌کند. این معمولاً یک مدیر رمز عبور مانند مدیر رمز عبور گوگل یا یک کلید امنیتی است.
فرآیند ایجاد و ثبت رمز عبور
فرآیند ایجاد و ثبت رمز عبور.

قبل از ایجاد رمز عبور، مطمئن شوید که سیستم این پیش‌نیازها را برآورده می‌کند:

  • حساب کاربری از طریق یک روش امن (مثلاً ایمیل، تأیید تلفن یا ادغام هویت) در یک بازه زمانی کوتاه و معنادار تأیید می‌شود.

  • رابط کاربری (frontend) و رابط کاربری (backend) می‌توانند به صورت امن با یکدیگر ارتباط برقرار کنند تا داده‌های مربوط به اعتبارنامه‌ها را تبادل کنند.

  • این مرورگر از WebAuthn و ایجاد رمز عبور پشتیبانی می‌کند.

ما می‌توانیم در بخش‌های بعدی نحوه بررسی اکثر آنها را به شما نشان دهیم.

زمانی که سیستم این شرایط را برآورده کند، فرآیند زیر برای ایجاد کلید عبور اتفاق می‌افتد:

  1. سیستم فرآیند ایجاد کلید عبور را زمانی آغاز می‌کند که کاربر اقدامی را آغاز کند (برای مثال، کلیک روی دکمه «ایجاد کلید عبور» در صفحه مدیریت کلید عبور خود یا پس از اتمام ثبت نام).
  2. بخش frontend داده‌های لازم برای اعتبارسنجی را از backend درخواست می‌کند، از جمله اطلاعات کاربر، یک چالش و شناسه‌های اعتبارسنجی برای جلوگیری از تکرار.
  3. فرانت‌اند، تابع navigator.credentials.create() را فراخوانی می‌کند تا از ارائه‌دهنده‌ی رمز عبور دستگاه بخواهد با استفاده از اطلاعات بک‌اند، یک رمز عبور تولید کند. توجه داشته باشید که این فراخوانی یک promise را برمی‌گرداند.
  4. دستگاه کاربر با استفاده از یک روش بیومتریک، پین یا الگو، کاربر را برای ایجاد کلید عبور احراز هویت می‌کند.
  5. ارائه‌دهنده‌ی کلید عبور، یک کلید عبور ایجاد می‌کند و یک اعتبارنامه‌ی کلید عمومی را به فرانت‌اند برمی‌گرداند و promise را حل می‌کند.
  6. بخش frontend، اعتبارنامه کلید عمومی تولید شده را به backend ارسال می‌کند.
  7. بک‌اند، کلید عمومی و سایر داده‌های مهم را برای احراز هویت در آینده ذخیره می‌کند.
  8. بخش مدیریت، کاربر را (مثلاً با استفاده از ایمیل) مطلع می‌کند تا ایجاد رمز عبور را تأیید کرده و دسترسی غیرمجاز احتمالی را تشخیص دهد.

این فرآیند، یک فرآیند ثبت رمز عبور امن و یکپارچه را برای کاربران تضمین می‌کند.

سازگاری‌ها

اکثر مرورگرها از WebAuthn پشتیبانی می‌کنند، البته با برخی شکاف‌های جزئی. برای جزئیات سازگاری مرورگر و سیستم عامل به passkeys.dev مراجعه کنید.

یک رمز عبور جدید ایجاد کنید

برای ایجاد یک رمز عبور جدید، این فرآیندی است که frontend باید دنبال کند:

  1. سازگاری را بررسی کنید.
  2. اطلاعات را از بک‌اند دریافت کنید.
  3. برای ایجاد یک رمز عبور، WebAuth API را فراخوانی کنید.
  4. کلید عمومی برگردانده شده را به backend ارسال کنید.
  5. اعتبارنامه را ذخیره کنید.

بخش‌های بعدی نشان می‌دهند که چگونه می‌توانید این کار را انجام دهید.

بررسی سازگاری

قبل از نمایش دکمه‌ی «ایجاد رمز عبور جدید»، ظاهر سایت باید موارد زیر را بررسی کند:

  • مرورگر از WebAuthn با PublicKeyCredential پشتیبانی می‌کند.

Browser Support

  • کروم: ۶۷.
  • لبه: ۱۸.
  • فایرفاکس: ۶۰.
  • سافاری: ۱۳.

Source

  • مرورگر از تشخیص قابلیت با استفاده از PublicKeyCredential.getClientCapabilities() پشتیبانی می‌کند.

Browser Support

  • کروم: ۱۳۳.
  • لبه: ۱۳۳.
  • فایرفاکس: ۱۳۵.
  • سافاری: ۱۷.۴.

Source

  • مرورگر از رابط کاربری شرطی WebAuthn با conditionalGet پشتیبانی می‌کند.

  • این دستگاه از یک احراز هویت پلتفرم (که می‌تواند یک کلید عبور ایجاد کرده و روی دستگاه احراز هویت کند) با passkeyPlatformAuthenticator پشتیبانی می‌کند.

قطعه کد زیر نشان می‌دهد که چگونه می‌توانید قبل از نمایش گزینه‌های مربوط به رمز عبور، سازگاری را بررسی کنید.

if (window.PublicKeyCredential && PublicKeyCredential.getClientCapabilities) {
  const capabilities = await PublicKeyCredential.getClientCapabilities();
  if (capabilities.conditionalGet === true &&
      capabilities.passkeyPlatformAuthenticator === true) {
    // The browser supports passkeys and the conditional UI.
  }
}

در این مثال، دکمه‌ی «ایجاد رمز عبور جدید» فقط در صورتی نمایش داده می‌شود که تمام شرایط برقرار باشد.

دریافت اطلاعات از بک‌اند

وقتی کاربر روی دکمه کلیک می‌کند، اطلاعات مورد نیاز را از backend دریافت می‌کند تا 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 (شناسه طرف وابسته) ، یک دامنه و یک وب‌سایت می‌توانند دامنه یا یک پسوند قابل ثبت را مشخص کنند. برای مثال، اگر مبدا یک RP، https://login.example.com:1337 باشد، شناسه RP می‌تواند login.example.com یا example.com باشد. اگر شناسه RP به صورت example.com مشخص شود، کاربر می‌تواند در login.example.com یا در هر زیردامنه در example.com احراز هویت کند. برای اطلاعات بیشتر در این مورد، به بخش « اجازه استفاده مجدد از کلید عبور در سایت‌های شما با درخواست‌های مبدا مرتبط» مراجعه کنید.
  • rp.name : نام RP (طرف اتکاکننده). این مورد در WebAuthn L3 منسوخ شده است، اما به دلایل سازگاری گنجانده شده است.
  • user.id : یک شناسه کاربری منحصر به فرد در ArrayBuffer که پس از ایجاد حساب کاربری ایجاد می‌شود. این شناسه باید دائمی باشد، برخلاف نام کاربری که ممکن است قابل ویرایش باشد. شناسه کاربری یک حساب کاربری را مشخص می‌کند، اما نباید حاوی هیچ گونه اطلاعات شخصی قابل شناسایی (PII) باشد. احتمالاً شما از قبل یک شناسه کاربری در سیستم خود دارید، اما در صورت نیاز، یک شناسه مخصوص برای کلیدهای عبور ایجاد کنید تا از هرگونه اطلاعات شخصی قابل شناسایی ( 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 ، در صورت ارائه، باید حاوی نتیجه‌ی فراخوانی getTransports() در طول ثبت هر اعتبارنامه باشد.
  • authenticatorSelection.authenticatorAttachment : اگر این ایجاد کلید عبور، ارتقاء از یک رمز عبور است، به عنوان مثال در یک تبلیغ پس از ورود به سیستم، این را روی "platform" به همراه hint: ['client-device'] تنظیم کنید "platform" نشان می‌دهد که RP یک تأییدکننده پلتفرم (یک تأییدکننده تعبیه شده در دستگاه پلتفرم) می‌خواهد که مثلاً وارد کردن کلید امنیتی USB را درخواست نکند. کاربر گزینه ساده‌تری برای ایجاد کلید عبور دارد.
  • authenticatorSelection.requireResidentKey : آن را روی مقدار بولی true تنظیم کنید. یک اعتبارنامه قابل کشف (کلید مقیم) اطلاعات کاربر را در کلید عبور ذخیره می‌کند و به کاربران اجازه می‌دهد تا پس از احراز هویت، حساب را انتخاب کنند.
  • authenticatorSelection.userVerification : نشان می‌دهد که آیا تأیید کاربر با استفاده از قفل صفحه نمایش دستگاه "required" ، "preferred" یا "discouraged" است. مقدار پیش‌فرض "preferred" است، به این معنی که تأییدکننده ممکن است از تأیید کاربر صرف نظر کند. این ویژگی را روی "preferred" تنظیم کنید یا آن را حذف کنید.

توصیه می‌کنیم شیء را روی سرور بسازید، ArrayBuffer را با Base64URL کدگذاری کنید و آن را از frontend دریافت کنید. به این ترتیب، می‌توانید payload را با استفاده از 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);
...

فراخوانی API وب‌اتن برای ایجاد کلید عبور

برای ایجاد یک رمز عبور جدید، تابع navigator.credentials.create() را فراخوانی کنید. API یک promise را برمی‌گرداند و منتظر تعامل کاربر برای نمایش یک کادر محاوره‌ای modal می‌ماند.

Browser Support

  • کروم: ۶۰.
  • لبه: ۱۸.
  • فایرفاکس: ۶۰.
  • سافاری: ۱۳.

Source

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

اعتبارنامه کلید عمومی بازگردانده شده را به backend ارسال کنید

پس از تأیید کاربر با استفاده از قفل صفحه نمایش دستگاه، یک کلید عبور ایجاد می‌شود و promise با بازگرداندن یک شیء PublicKeyCredential به frontend، حل می‌شود.

این promise می‌تواند به دلایل مختلف رد شود. شما می‌توانید با بررسی ویژگی name شیء Error ، این خطاها را مدیریت کنید:

  • InvalidStateError : یک رمز عبور از قبل روی دستگاه وجود دارد. هیچ پنجره خطایی به کاربر نشان داده نمی‌شود. سایت نباید این را به عنوان خطا در نظر بگیرد. کاربر می‌خواست دستگاه محلی ثبت شود و این اتفاق افتاد.
  • NotAllowedError : کاربر عملیات را لغو کرده است.
  • AbortError : عملیات لغو شد.
  • سایر استثنائات : اتفاق غیرمنتظره‌ای رخ داده است. مرورگر یک کادر محاوره‌ای خطا به کاربر نشان می‌دهد.

شیء اعتبارنامه کلید عمومی شامل ویژگی‌های زیر است:

  • id : یک شناسه رمزگذاری شده Base64URL از کلید عبور ایجاد شده. این شناسه به مرورگر کمک می‌کند تا هنگام احراز هویت، تشخیص دهد که آیا کلید عبور منطبق در دستگاه وجود دارد یا خیر. این مقدار باید در پایگاه داده در backend ذخیره شود.
  • rawId : یک نسخه ArrayBuffer از شناسه اعتبارنامه.
  • response.clientDataJSON : یک داده کلاینت کدگذاری شده توسط ArrayBuffer.
  • response.attestationObject : یک شیء گواهی کدگذاری شده توسط ArrayBuffer. این شیء حاوی اطلاعات مهمی مانند شناسه RP، پرچم‌ها و کلید عمومی است.
  • 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
});
...

اعتبارنامه را ذخیره کنید

پس از دریافت اعتبارنامه کلید عمومی در backend، توصیه می‌کنیم به جای نوشتن کد خودتان برای پردازش اعتبارنامه کلید عمومی، از یک کتابخانه یا راهکار سمت سرور استفاده کنید .

سپس می‌توانید اطلاعات بازیابی شده از اعتبارنامه را برای استفاده‌های بعدی در پایگاه داده ذخیره کنید.

لیست زیر شامل ویژگی‌های پیشنهادی برای ذخیره است:

  • شناسه اعتبارنامه : شناسه اعتبارنامه‌ای که به همراه کلید عمومی اعتبارنامه برگردانده شده است.
  • نام اعتبارنامه : نام اعتبارنامه. آن را بر اساس ارائه‌دهنده‌ی کلید عبوری که ایجاد شده است، نامگذاری کنید که بر اساس AAGUID قابل شناسایی است .
  • شناسه کاربری : شناسه کاربری که برای ایجاد رمز عبور استفاده شده است.
  • کلید عمومی : کلید عمومی که به همراه اعتبارنامه کلید عمومی برگردانده شده است. این برای تأیید ادعای رمز عبور لازم است.
  • تاریخ و زمان ایجاد : تاریخ و زمان ایجاد کلید عبور را ثبت کنید. این برای شناسایی کلید عبور مفید است.
  • تاریخ و زمان آخرین استفاده : آخرین تاریخ و زمانی را که کاربر از کلید عبور برای ورود به سیستم استفاده کرده است، ثبت می‌کند. این برای تعیین اینکه کاربر از کدام کلید عبور استفاده کرده (یا استفاده نکرده است) مفید است.
  • AAGUID : شناسه‌ی منحصر به فرد ارائه‌دهنده‌ی کلید عبور.
  • پرچم واجد شرایط بودن برای پشتیبان‌گیری : اگر دستگاه واجد شرایط همگام‌سازی کلید عبور باشد، مقدار آن درست است. این اطلاعات به کاربران کمک می‌کند تا کلیدهای عبور قابل همگام‌سازی و کلیدهای عبور متصل به دستگاه (غیرقابل همگام‌سازی) را در صفحه مدیریت کلید عبور شناسایی کنند.

دستورالعمل‌های دقیق‌تر را در ثبت رمز عبور سمت سرور دنبال کنید.

در صورت عدم موفقیت ثبت نام، سیگنال دهید

اگر ثبت کلید عبور با شکست مواجه شود، ممکن است باعث سردرگمی کاربر شود. اگر کلید عبور در ارائه دهنده کلید عبور وجود داشته باشد و برای کاربر در دسترس باشد، اما کلید عمومی مرتبط در سمت سرور ذخیره نشده باشد، تلاش برای ورود به سیستم با استفاده از کلید عبور هرگز موفقیت آمیز نخواهد بود و عیب یابی آن دشوار است. در این صورت، حتماً به کاربر اطلاع دهید.

برای جلوگیری از چنین شرایطی، می‌توانید با استفاده از API سیگنال، یک کلید عبور ناشناخته را به ارائه‌دهنده کلید عبور ارسال کنید . با فراخوانی PublicKeyCredential.signalUnknownCredential() با یک شناسه RP و یک شناسه اعتبارنامه، RP می‌تواند به ارائه‌دهنده کلید عبور اطلاع دهد که اعتبارنامه مشخص شده حذف شده یا وجود ندارد. نحوه برخورد با این سیگنال به ارائه‌دهنده کلید عبور بستگی دارد، اما در صورت پشتیبانی، انتظار می‌رود کلید عبور مرتبط حذف شود.

// 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.
    ...
  }
}

برای کسب اطلاعات بیشتر در مورد API سیگنال، بخش «کلیدهای عبور را با اعتبارنامه‌های روی سرور خود با API سیگنال سازگار نگه دارید» را مطالعه کنید.

ارسال اعلان به کاربر

ارسال اعلان (مانند ایمیل) هنگام ثبت رمز عبور، به کاربران کمک می‌کند تا دسترسی غیرمجاز به حساب را تشخیص دهند. اگر مهاجمی بدون اطلاع کاربر، رمز عبور ایجاد کند، حتی پس از تغییر رمز عبور، رمز عبور برای سوءاستفاده‌های بعدی در دسترس باقی می‌ماند. این اعلان به کاربر هشدار می‌دهد و به جلوگیری از این امر کمک می‌کند.

چک لیست

  • قبل از اینکه به کاربر اجازه ایجاد رمز عبور بدهید، اعتبار او را تأیید کنید (ترجیحاً از طریق ایمیل یا یک روش امن).
  • با استفاده از excludeCredentials از ایجاد کلیدهای عبور تکراری برای یک ارائه‌دهنده کلید عبور مشابه جلوگیری کنید.
  • AAGUID را برای شناسایی ارائه‌دهنده‌ی کلید عبور و نام‌گذاری اعتبارنامه‌ی کاربر ذخیره کنید.
  • اگر تلاش برای ثبت رمز عبور با شکست مواجه شد، با استفاده از PublicKeyCredential.signalUnknownCredential() سیگنال دهید.
  • پس از ایجاد و ثبت رمز عبور برای حساب کاربری، یک اعلان برای کاربر ارسال کنید.

منابع

مرحله بعدی: با استفاده از رمز عبور از طریق تکمیل خودکار فرم وارد شوید .