מפתחות גישה הופכים את חשבונות המשתמשים לבטוחים יותר, פשוטים וקלים יותר לשימוש.
שימוש במפתחות גישה במקום בסיסמאות הוא דרך מצוינת שבזכותה אתרים יהיו בטוחים, פשוטים וקלים יותר לשימוש וללא סיסמאות. עם מפתח גישה, משתמשים יכולים להיכנס לאתר או לאפליקציה באמצעות טביעת אצבע, קוד אימות או קוד אימות של המכשיר.
יש ליצור מפתח גישה שמשויך לחשבון משתמש ולשמור את המפתח הציבורי שלו בשרת כדי שמשתמש יוכל להיכנס באמצעותו.
איך זה עובד
אפשר לבקש מהמשתמשים ליצור מפתח גישה באחד מהמצבים הבאים:
- כשמשתמש נכנס לחשבון באמצעות סיסמה.
- כשמשתמש נכנס באמצעות מפתח גישה ממכשיר אחר (כלומר,
authenticatorAttachment
הואcross-platform
). - בדף ייעודי שבו המשתמשים יכולים לנהל את מפתחות הגישה שלהם.
כדי ליצור מפתח גישה, צריך להשתמש ב-WebAuthn API.
ארבעת הרכיבים בתהליך הרישום של מפתחות הגישה:
- הקצה העורפי: שרת העורפי שמכיל את מסד הנתונים של החשבונות שבו נשמר המפתח הציבורי ומטא-נתונים נוספים לגבי מפתח הגישה.
- הקצה הקדמי: הקצה הקדמי שלכם מתקשר עם הדפדפן ושולח בקשות שליפה לקצה העורפי.
- דפדפן: הדפדפן של המשתמש שמריץ את JavaScript.
- Authenticator: רכיב האימות של המשתמש שיוצר ומאחסן את מפתח הגישה. זה יכול לכלול את מנהל הסיסמאות באותו מכשיר שבו נמצא הדפדפן (לדוגמה, כשמשתמשים ב-Windows Hello) או במכשיר אחר, כמו טלפון.
כך מוסיפים מפתח גישה חדש לחשבון משתמש קיים:
- משתמש נכנס לאתר.
- אחרי שהמשתמש נכנס לחשבון, הוא מבקש ליצור מפתח גישה בממשק הקדמי. לדוגמה, על ידי לחיצה על הלחצן 'יצירת מפתח גישה'.
- הקצה העורפי מבקש מידע מהקצה העורפי כדי ליצור מפתח גישה, כמו פרטי משתמש, אתגר והמזהים של פרטי הכניסה שצריך להחריג.
- החזית קוראת ל-
navigator.credentials.create()
ליצור מפתח גישה. השיחה מחזירה הבטחה. - מפתח הגישה נוצר אחרי שהמשתמש מביע הסכמה באמצעות השיטה לביטול נעילת המסך של המכשיר. ההבטחה נפתרה, ופרטי הכניסה של המפתח הציבורי מוחזרים לממשק הקצה.
- הקצה הקדמי שולח את פרטי הכניסה של המפתח הציבורי לקצה העורפי ומאחסן את מזהה פרטי הכניסה ואת המפתח הציבורי שמשויך לחשבון המשתמש לצורך אימותים עתידיים.
תאימות
רוב הדפדפנים תומכים ב-WebAuthn, אבל יש פערים קטנים. במאמר תמיכה במכשירים – keys.dev מוסבר איזה שילוב של דפדפנים ומערכות הפעלה תומכים ביצירת מפתח גישה.
יצירת מפתח גישה חדש
כך ממשק קצה צריך לפעול בעקבות בקשה ליצירת מפתח גישה חדש.
זיהוי תכונות
לפני שמציגים את הלחצן 'יצירת מפתח גישה חדש', צריך לבדוק אם:
- הדפדפן תומך ב-WebAuthn עם
PublicKeyCredential
.
- המכשיר תומך בכלי לאימות פלטפורמה (שיכול ליצור מפתח גישה ולבצע אימות באמצעות מפתח הגישה) באמצעות
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.
- הדפדפן תומך בממשק משתמש מותנה של WebAuthn עם
PublicKeyCredenital.isConditionalMediationAvailable()
.
// 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()
מהקצה העורפי:
challenge
: אתגר שנוצר על ידי שרת ב-ArayBuffer עבור הרישום הזה. זהו שדה חובה, אבל לא משתמשים בו במהלך הרישום, אלא אם מבצעים אימות – נושא מתקדם שלא מופיע כאן.user.id
: המזהה הייחודי של המשתמש. הערך הזה חייב להיות ArrayBuffer שלא כולל פרטים אישיים מזהים, כמו כתובות אימייל או שמות משתמשים. אם יוצרים ערך אקראי של 16 בייט לכל חשבון, צריך לפעול בצורה תקינה.user.name
: השדה הזה צריך להכיל מזהה ייחודי של החשבון שהמשתמש יזהה, כמו כתובת האימייל או שם המשתמש. ההערה תוצג בבורר החשבונות. (אם אתם משתמשים בשם משתמש, השתמשו באותו ערך כמו באימות הסיסמה).user.displayName
: שדה זה הוא שם חובה וידידותי יותר למשתמש עבור החשבון. הוא לא חייב להיות ייחודי ויכול להיות השם שהמשתמש בחר. אם לאתר אין ערך שמתאים לכלול כאן, צריך להעביר מחרוזת ריקה. יכול להיות שהחשבון הזה יוצג בבורר החשבונות, בהתאם לדפדפן.excludeCredentials
: מונע רישום של אותו מכשיר באמצעות רשימה של מזהים של פרטי כניסה רשומים שכבר רשומים. אם צוין מינויtransports
, הוא צריך לכלול את תוצאת הקריאה ל-getTransports()
במהלך הרישום של כל פרטי כניסה.
קריאה ל-WebAuthn API כדי ליצור מפתח גישה
צריך להתקשר אל navigator.credentials.create()
כדי ליצור מפתח גישה חדש. ה-API מחזיר הבטחה שבהמתנה לאינטראקציה של המשתמש עם תיבת דו-שיח של חלון.
const publicKeyCredentialCreationOptions = {
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,
}
};
const credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreationOptions
});
// Encode and send the credential to the server for verification.
הפרמטרים שלא מוסברים למעלה הם:
rp.id
: מזהה RP הוא דומיין והאתר יכול לציין את הדומיין שלו או סיומת שאפשר לרשום. לדוגמה, אם המקור של הגורם המוגבל הואhttps://login.example.com:1337
, מזהה הגורם המוגבל (RP) יכול להיותlogin.example.com
אוexample.com
. אם מזהה הגורם המוגבל (RP) מצוין בתורexample.com
, המשתמש יכול לבצע אימות ב-login.example.com
או בכל תת-דומיינים ב-example.com
.rp.name
: השם של הגורם המוגבל.pubKeyCredParams
: השדה הזה מציין מהם האלגוריתמים הנתמכים של מפתח ציבורי של הגורם המוגבל. מומלץ להגדיר אותו ל-[{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]
. כך אנחנו מציינים תמיכה ב-ECDSA עם P-256 ו-RSA PKCS#1, והתמיכה בהם מספקת כיסוי מלא.authenticatorSelection.authenticatorAttachment
: צריך להגדיר את הערך ל-"platform"
אם היצירה של מפתח הגישה היא שדרוג מסיסמה, למשל במסגרת מבצע אחרי כניסה."platform"
מציין שה-RP רוצה מאמת פלטפורמה (מאמת שמוטמע במכשיר של הפלטפורמה) שלא יציג בקשה להכניס אותו, למשל מפתח אבטחה בחיבור USB. למשתמשים יש אפשרות פשוטה יותר ליצור מפתח גישה.authenticatorSelection.requireResidentKey
: מגדירים את הפרמטר כ-'true' בוליאני. פרטי כניסה גלויים (מפתח תושב) מאחסנים את פרטי המשתמש במפתח הגישה ומאפשרים למשתמשים לבחור את החשבון במהלך האימות. מידע נוסף על פרטי כניסה שגלויים לכולם זמין במאמר סקירה מפורטת של פרטי כניסה שגלויים לכולם.authenticatorSelection.userVerification
: מציין אם אימות המשתמש באמצעות נעילת המסך של המכשיר הוא"required"
,"preferred"
או"discouraged"
. ברירת המחדל היא"preferred"
, והמאמת יכול לדלג על אימות המשתמש. צריך להגדיר את הערך הזה כ-"preferred"
או להשמיט את הנכס.
שליחת פרטי הכניסה של המפתח הציבורי שהוחזרו לקצה העורפי
אחרי שהמשתמש מביע הסכמה לשימוש בנעילת המסך של המכשיר, נוצר מפתח גישה ומוסבר בהבטחה איך להחזיר אובייקט PublicKeyCredential לממשק הקצה.
אפשר לדחות את ההבטחה מסיבות שונות. כדי לטפל בשגיאות האלו, אפשר לבדוק את המאפיין name
של האובייקט Error
:
InvalidStateError
: כבר קיים מפתח גישה במכשיר. לא תוצג למשתמש תיבת דו-שיח עם שגיאות, והאתר לא יתייחס אליה כשגיאה – המשתמש רצה שהמכשיר המקומי יירשם והוא אכן זה.NotAllowedError
: המשתמש ביטל את הפעולה.- חריגים אחרים: קרה משהו בלתי צפוי. הדפדפן מציג למשתמש תיבת דו-שיח עם שגיאה.
האובייקט של פרטי הכניסה של המפתח הציבורי מכיל את המאפיינים הבאים:
id
: מזהה בקידוד Base64URL של מפתח הגישה שנוצר. המזהה הזה עוזר לדפדפן לקבוע אם יש במכשיר מפתח גישה תואם במהלך האימות. צריך לאחסן את הערך הזה במסד הנתונים בקצה העורפי.rawId
: גרסת ArrayBuffer של מזהה פרטי הכניסה.response.clientDataJSON
: נתוני לקוח בקידוד ArrayBuffer.response.attestationObject
: אובייקט אימות (attestation) מקודד של ArrayBuffer. הפעולה הזו מכילה מידע חשוב כמו מזהה הגורם המוגבל, דגלים ומפתח ציבורי.authenticatorAttachment
: מוחזרת הערך"platform"
כשפרטי הכניסה האלה נוצרים במכשיר שתומך במפתח גישה.type
: השדה הזה תמיד מוגדר ל-"public-key"
.
אם אתם משתמשים בספרייה כדי לטפל באובייקט של פרטי הכניסה של המפתח הציבורי בקצה העורפי, מומלץ לשלוח את האובייקט כולו לקצה העורפי אחרי הקידוד שלו באופן חלקי באמצעות base64url.
שמירת פרטי הכניסה
אחרי קבלת פרטי הכניסה של המפתח הציבורי בקצה העורפי, מעבירים אותם לספריית FIDO כדי לעבד את האובייקט.
אחר כך אפשר לאחסן במסד הנתונים את המידע שמאוחזר מפרטי הכניסה, לשימוש עתידי. הרשימה הבאה כוללת כמה מאפיינים אופייניים לשמירה:
- מזהה פרטי הכניסה (מפתח ראשי)
- User ID
- מפתח ציבורי
פרטי הכניסה של המפתח הציבורי כוללים גם את הפרטים הבאים שאולי תרצו לשמור במסד הנתונים:
- סימון זכאות לגיבוי:
true
אם המכשיר עומד בדרישות לסנכרון עם מפתח הגישה. - דגל מצב הגיבוי:
true
אם מפתח הגישה שנוצר מוגדר בפועל לסנכרון. - אמצעי תחבורה:
רשימת ההעברות שהמכשיר תומך בהן:
"internal"
פירושו שהמכשיר תומך במפתח גישה,"hybrid"
פירושו שהוא תומך גם באימות במכשיר אחר.
הוראות מפורטות יותר זמינות במאמר רישום מפתח גישה בצד השרת
כדי לאמת את המשתמש, קראו את המאמר כניסה באמצעות מפתח גישה באמצעות מילוי אוטומטי של טופס.