יצירת מפתח גישה להתחברות ללא סיסמה

מפתחות גישה הופכים את חשבונות המשתמשים לבטוחים יותר, פשוטים וקלים יותר לשימוש.

שימוש במפתחות גישה במקום בסיסמאות הוא דרך מצוינת שבזכותה אתרים יהיו בטוחים, פשוטים וקלים יותר לשימוש וללא סיסמאות. עם מפתח גישה, משתמשים יכולים להיכנס לאתר או לאפליקציה באמצעות טביעת אצבע, קוד אימות או קוד אימות של המכשיר.

יש ליצור מפתח גישה שמשויך לחשבון משתמש ולשמור את המפתח הציבורי שלו בשרת כדי שמשתמש יוכל להיכנס באמצעותו.

איך זה עובד

אפשר לבקש מהמשתמשים ליצור מפתח גישה באחד מהמצבים הבאים:

  • כשמשתמש נכנס לחשבון באמצעות סיסמה.
  • כשמשתמש נכנס באמצעות מפתח גישה ממכשיר אחר (כלומר, authenticatorAttachment הוא cross-platform).
  • בדף ייעודי שבו המשתמשים יכולים לנהל את מפתחות הגישה שלהם.

כדי ליצור מפתח גישה, צריך להשתמש ב-WebAuthn API.

ארבעת הרכיבים בתהליך הרישום של מפתחות הגישה:

  • הקצה העורפי: שרת העורפי שמכיל את מסד הנתונים של החשבונות שבו נשמר המפתח הציבורי ומטא-נתונים נוספים לגבי מפתח הגישה.
  • הקצה הקדמי: הקצה הקדמי שלכם מתקשר עם הדפדפן ושולח בקשות שליפה לקצה העורפי.
  • דפדפן: הדפדפן של המשתמש שמריץ את JavaScript.
  • Authenticator: רכיב האימות של המשתמש שיוצר ומאחסן את מפתח הגישה. זה יכול להיות באותו מכשיר שבו נמצא הדפדפן (לדוגמה, כשמשתמשים ב-Windows Hello) או במכשיר אחר, כמו טלפון.
תרשים רישום של מפתח גישה

כך מוסיפים מפתח גישה חדש לחשבון משתמש קיים:

  1. משתמש נכנס לאתר.
  2. אחרי שהמשתמש נכנס לחשבון, הוא מבקש ליצור מפתח גישה בממשק הקדמי. לדוגמה, על ידי לחיצה על הלחצן 'יצירת מפתח גישה'.
  3. הקצה העורפי מבקש מידע מהקצה העורפי כדי ליצור מפתח גישה, כמו פרטי משתמש, אתגר והמזהים של פרטי הכניסה שצריך להחריג.
  4. החזית קוראת ל-navigator.credentials.create() ליצור מפתח גישה. השיחה מחזירה הבטחה.
  5. מפתח הגישה נוצר אחרי שהמשתמש מביע הסכמה באמצעות השיטה לביטול נעילת המסך של המכשיר. ההבטחה נפתרה, ופרטי הכניסה של המפתח הציבורי מוחזרים לממשק הקצה.
  6. הקצה הקדמי שולח את פרטי הכניסה של המפתח הציבורי לקצה העורפי ומאחסן את מזהה פרטי הכניסה ואת המפתח הציבורי שמשויך לחשבון המשתמש לצורך אימותים עתידיים.

תאימות

רוב הדפדפנים תומכים ב-WebAuthn, אבל יש פערים קטנים. במאמר תמיכה במכשירים – keys.dev מוסבר איזה שילוב של דפדפנים ומערכות הפעלה תומכים ביצירת מפתח גישה.

יצירת מפתח גישה חדש

כך ממשק קצה צריך לפעול בעקבות בקשה ליצירת מפתח גישה חדש.

זיהוי תכונות

לפני שמציגים את הלחצן 'יצירת מפתח גישה חדש', צריך לבדוק אם:

  • הדפדפן תומך ב-WebAuthn.
  • המכשיר תומך בכלי לאימות פלטפורמה (יכול ליצור מפתח גישה ולבצע אימות באמצעות מפתח הגישה).
  • הדפדפן תומך בממשק משתמש מותנה של WebAuthn.
// 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: אתגר שנוצר על ידי שרת ב-ArrayBuffer עבור הרישום הזה. זהו שדה חובה אבל לא משתמשים בו במהלך הרישום, אלא אם מבצעים אימות – נושא מתקדם שלא מופיע כאן.
  • 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
  • מפתח ציבורי

פרטי הכניסה של המפתח הציבורי כוללים גם את הפרטים הבאים שאולי תרצו לשמור במסד הנתונים:

כדי לאמת את המשתמש, קראו את המאמר כניסה באמצעות מפתח גישה באמצעות מילוי אוטומטי של טופס.

מקורות מידע