יוצרים תהליך כניסה שמשתמש במפתחות גישה, תוך התחשבות במשתמשים שכבר משתמשים בסיסמאות.
מפתחות גישה מחליפים את הסיסמאות ומאפשרים להשתמש בחשבונות משתמשים באינטרנט בצורה בטוחה, פשוטה ונוחה יותר. עם זאת, המעבר מאימות שמבוסס על סיסמה לאימות שמבוסס על מפתח גישה עלול לסבך את חוויית המשתמש. שימוש במילוי אוטומטי של טפסים כדי להציע מפתחות גישה יכול לעזור ליצור חוויה אחידה.
למה כדאי להשתמש במילוי אוטומטי של טפסים כדי להיכנס לחשבון באמצעות מפתח גישה?
מפתח גישה מאפשר למשתמש להיכנס לאתר רק באמצעות טביעת אצבע, זיהוי פנים או קוד אימות של המכשיר.
באופן אידיאלי, לא יהיו משתמשים עם סיסמה ותהליך האימות יכול להיות פשוט כמו לחצן כניסה יחיד. כשהמשתמש מקשיב על הלחצן, מופיעה תיבת דו-שיח לבחירת חשבון. המשתמש יכול לבחור חשבון, לבטל את נעילת המסך כדי לאמת ולהיכנס.
עם זאת, המעבר מסיסמה לאימות שמבוסס על מפתח גישה יכול להיות מאתגר. כשהמשתמשים יעברו למפתחות גישה, עדיין יהיו כאלה שישתמשו בסיסמאות, והאתרים יצטרכו להתאים את עצמם לשני סוגי המשתמשים. לא סביר שהמשתמשים עצמם יזכרו באילו אתרים הם עברו להשתמש במפתחות גישה, ולכן בקשה מהם לבחור מראש את השיטה שבה הם רוצים להשתמש תהיה חוויית משתמש גרועה.
גם מפתחות הגישה הם טכנולוגיה חדשה. הסבר על התכונות האלה ולוודא שהמשתמשים מרגישים בנוח להשתמש בהן יכולים להיות אתגר לאתרים. כדי לפתור את שתי הבעיות, אנחנו יכולים להסתמך על חוויות משתמש מוכרות של מילוי אוטומטי של סיסמאות.
ממשק משתמש מותנה
כדי ליצור חוויית משתמש יעילה גם למשתמשים עם מפתחות גישה וגם למשתמשים עם סיסמאות, תוכלו לכלול מפתחות גישה בהצעות למילוי אוטומטי. התכונה הזו נקראת ממשק משתמש מותנה והיא חלק מתקן WebAuthn.
ברגע שהמשתמש מקשיב על שדה הקלט של שם המשתמש, תיפתח תיבת דו-שיח עם הצעות למילוי אוטומטי, שבה מודגשים מפתחות הגישה השמורים לצד הצעות למילוי אוטומטי של סיסמה. לאחר מכן, המשתמש יוכל לבחור חשבון ולהשתמש בנעילת המסך של המכשיר כדי להיכנס.
כך המשתמשים יוכלו להיכנס לאתר שלכם באמצעות הטופס הקיים כאילו לא השתנה דבר, אבל עם היתרון הנוסף של מפתחות הגישה מבחינת האבטחה, אם יש להם מפתח כזה.
איך זה עובד
כדי לבצע אימות באמצעות מפתח גישה, משתמשים ב-WebAuthn API.
ארבעת הרכיבים בתהליך האימות באמצעות מפתח גישה הם: המשתמש:
- קצה עורפי: שרת הקצה העורפי שמכיל את מסד הנתונים של החשבונות שבו מאוחסנים המפתח הציבורי ומטא-נתונים אחרים לגבי מפתח הגישה.
- חזית: החזית שלכם, שמתקשרת עם הדפדפן ושולחת בקשות אחזור לקצה העורפי.
- דפדפן: הדפדפן של המשתמש שבו פועל הקוד של JavaScript.
- מאמת: מאמת החשבונות של המשתמש שיוצר ומאחסן את מפתח הגישה. המכשיר יכול להיות באותו מכשיר שבו פועל הדפדפן (למשל, כשמשתמשים ב-Windows Hello) או במכשיר אחר, כמו טלפון.
- ברגע שמשתמש מגיע לממשק הקצה, הוא מבקש מאתגר מהקצה העורפי כדי לבצע אימות באמצעות מפתח גישה, ומפעיל את
navigator.credentials.get()
כדי להתחיל את תהליך האימות באמצעות מפתח גישה. הפונקציה מחזירהPromise
. - כשהמשתמש מעביר את הסמן לשדה הכניסה, הדפדפן מציג תיבת דו-שיח למילוי אוטומטי של סיסמה, כולל מפתחות גישה. תיבת דו-שיח לאימות תופיע אם המשתמש יבחר מפתח גישה.
- אחרי שהמשתמש מאמת את הזהות שלו באמצעות נעילת המסך של המכשיר, ההתחייבות מתבטלת ומשתמשים מחזירים לפלטפורמת הקצה פרטי כניסה למפתח ציבורי.
- ממשק הקצה שולח את פרטי הכניסה של המפתח הציבורי לקצה העורפי. הקצה העורפי מאמת את החתימה מול המפתח הציבורי של החשבון התואם במסד הנתונים. אם הפעולה תצליח, המשתמש ייכנס לחשבון.
אימות באמצעות מפתח גישה באמצעות מילוי אוטומטי של טפסים
כשמשתמש רוצה להיכנס לחשבון, אפשר לבצע קריאה מותנית ל-WebAuthn get
כדי לציין שמפתחות גישה עשויים להיכלל בהצעות למילוי אוטומטי. קריאה מותנית ל-navigator.credentials.get()
API של WebAuthn לא מציגה ממשק משתמש ונשארת בהמתנה עד שהמשתמש בוחר חשבון להיכנס איתו מהצעות המילוי האוטומטי. אם המשתמש בוחר מפתח גישה, הדפדפן יפתור את ההבטחה באמצעות פרטי כניסה במקום למלא את טופס הכניסה. לאחר מכן, הדף אחראי להכניס את המשתמש.
הוספת הערה לשדה להזנת קלט בטופס
אם צריך, מוסיפים מאפיין autocomplete
לשדה input
של שם המשתמש.
מוסיפים את username
ו-webauthn
בתור האסימונים שלו כדי לאפשר לו להציע מפתחות גישה.
<input type="text" name="username" autocomplete="username webauthn" ...>
זיהוי תכונות
לפני שמפעילים קריאה מותנית ל-WebAuthn API, צריך לבדוק אם:
- הדפדפן תומך ב-WebAuthn באמצעות
PublicKeyCredential
.
- הדפדפן תומך בממשק משתמש מותנה של WebAuthn באמצעות
PublicKeyCredential.isConditionalMediationAvailable()
.
// Availability of `window.PublicKeyCredential` means WebAuthn is usable.
if (window.PublicKeyCredential &&
PublicKeyCredential.isConditionalMediationAvailable) {
// Check if conditional mediation is available.
const isCMA = await PublicKeyCredential.isConditionalMediationAvailable();
if (isCMA) {
// Call WebAuthn authentication
}
}
אחזור אתגר משרת ה-RP
אחזור את האתגר משרת ה-RP שנדרש כדי לבצע קריאה ל-navigator.credentials.get()
:
challenge
: אתגר שנוצר על ידי השרת ב-ArrayBuffer. הפעולה הזו נדרשת כדי למנוע התקפות של שליחה מחדש. חשוב ליצור אתגר חדש בכל ניסיון כניסה, ולהתעלם ממנו אחרי פרק זמן מסוים או אחרי שניסיון כניסה מסוים נכשל באימות. אפשר להתייחס אליו כאל אסימון CSRF.allowCredentials
: מערך של פרטי כניסה קבילים לאימות הזה. מעבירים מערך ריק כדי לאפשר למשתמש לבחור מפתח גישה זמין מתוך רשימה שמוצגת בדפדפן.userVerification
: מציין אם אימות המשתמש באמצעות נעילת המסך של המכשיר הוא"required"
, "preferred"
או"discouraged"
. ברירת המחדל היא"preferred"
, כלומר האימות עשוי לדלג על אימות המשתמש. מגדירים את הערך"preferred"
או משמיטים את הנכס.
קריאה ל-WebAuthn API עם הדגל conditional
כדי לאמת את המשתמש
קוראים ל-navigator.credentials.get()
כדי להתחיל להמתין לאימות המשתמש.
// To abort a WebAuthn call, instantiate an `AbortController`.
const abortController = new AbortController();
const publicKeyCredentialRequestOptions = {
// Server generated challenge
challenge: ****,
// The same RP ID as used during registration
rpId: 'example.com',
};
const credential = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions,
signal: abortController.signal,
// Specify 'conditional' to activate conditional UI
mediation: 'conditional'
});
rpId
: מזהה RP הוא דומיין, ואתר יכול לציין את הדומיין שלו או סיומת שניתן לרשום. הערך הזה חייב להתאים לערך של rp.id ששימש ליצירת מפתח הגישה.
חשוב לציין את הערך mediation: 'conditional'
כדי להפוך את הבקשה לתנאי.
שולחים את פרטי הכניסה של המפתח הציבורי שהוחזרו לשרת ה-RP
אחרי שהמשתמש בוחר חשבון ומביע הסכמה באמצעות נעילת המסך של המכשיר, ההתחייבות מתקבלת ומוחזר אובייקט PublicKeyCredential
לקצה הקדמי של RP.
יש כמה סיבות אפשריות לדחייה של הבטחה. צריך לטפל בשגיאות בהתאם, בהתאם למאפיין name
של האובייקט Error
:
NotAllowedError
: המשתמש ביטל את הפעולה.- חריגות אחרות: קרה משהו לא צפוי. הדפדפן מציג למשתמש תיבת דו-שיח עם הודעת שגיאה.
אובייקט פרטי הכניסה של המפתח הציבורי מכיל את המאפיינים הבאים:
id
: המזהה בקידוד base64url של פרטי הכניסה המאומתים של מפתח הגישה.rawId
: גרסה של מזהה פרטי הכניסה ב-ArrayBuffer.response.clientDataJSON
: ArrayBuffer של נתוני לקוח. השדה הזה מכיל מידע כמו האתגר והמקור ששרת ה-RP יצטרך לאמת.response.authenticatorData
: ArrayBuffer של נתוני אימות. השדה הזה מכיל מידע כמו מזהה RP.response.signature
: ArrayBuffer של החתימה. הערך הזה הוא הליבה של פרטי הכניסה, וצריך לאמת אותו בשרת.response.userHandle
: ArrayBuffer שמכיל את מזהה המשתמש שהוגדר בזמן היצירה. אפשר להשתמש בערך הזה במקום במזהה פרטי הכניסה, אם השרת צריך לבחור את ערכי המזהה שבהם הוא משתמש, או אם הקצה העורפי רוצה להימנע מיצירת אינדקס של מזהי פרטי הכניסה.authenticatorAttachment
: הפונקציה מחזירה את הערךplatform
כשפרטי הכניסה האלה הגיעו מהמכשיר המקומי. אחרת,cross-platform
, במיוחד כשהמשתמש השתמש בטלפון כדי להיכנס לחשבון. אם המשתמש נאלץ להשתמש בטלפון כדי להיכנס לחשבון, כדאי לבקש ממנו ליצור מפתח גישה במכשיר המקומי.type
: השדה הזה תמיד מוגדר ל-"public-key"
.
אם אתם משתמשים בספרייה כדי לטפל באובייקט פרטי הכניסה של המפתח הציבורי בשרת ה-RP, מומלץ לשלוח את האובייקט כולו לשרת אחרי שמקודדים אותו באופן חלקי באמצעות base64url.
אימות החתימה
כשמקבלים את פרטי הכניסה של המפתח הציבורי בשרת, מעבירים אותם לספריית FIDO כדי לעבד את האובייקט.
מחפשים את מזהה פרטי הכניסה התואם באמצעות הנכס id
(אם צריך לקבוע את חשבון המשתמש, משתמשים בנכס userHandle
שהוא הערך של user.id
שציינתם כשיצרתם את פרטי הכניסה). בודקים אם אפשר לאמת את signature
של פרטי הכניסה באמצעות המפתח הציבורי המאוחסן. כדי לעשות זאת, מומלץ להשתמש בספרייה או בפתרון בצד השרת במקום לכתוב קוד משלכם. ספריות קוד פתוח זמינות במאגר GitHub של awesome-webauth.
אחרי שהפרטים המזהים מאומתים באמצעות מפתח ציבורי תואם, מביאים את המשתמש להתחברות.
הוראות מפורטות יותר זמינות במאמר אימות מפתח גישה בצד השרת