สร้างพาสคีย์สำหรับการเข้าสู่ระบบแบบไม่ใช้รหัสผ่าน

พาสคีย์ทำให้บัญชีผู้ใช้ปลอดภัยขึ้น ใช้งานง่ายขึ้น

การใช้พาสคีย์แทนรหัสผ่านเป็นวิธีที่ยอดเยี่ยมสำหรับเว็บไซต์ในการทำให้บัญชีผู้ใช้ปลอดภัยขึ้น ใช้งานง่ายขึ้น ใช้งานได้ง่ายขึ้น และไม่ต้องใช้รหัสผ่าน เมื่อใช้พาสคีย์ ผู้ใช้จะลงชื่อเข้าใช้เว็บไซต์หรือแอปได้โดยใช้เพียงลายนิ้วมือ ใบหน้า หรือ PIN ของอุปกรณ์

คุณต้องสร้างพาสคีย์ที่เชื่อมโยงกับบัญชีผู้ใช้และจัดเก็บคีย์สาธารณะไว้ในเซิร์ฟเวอร์ก่อน ผู้ใช้จึงจะลงชื่อเข้าใช้ด้วยพาสคีย์ได้

วิธีการทำงาน

ระบบอาจขอให้ผู้ใช้สร้างพาสคีย์ในสถานการณ์ต่อไปนี้

  • เมื่อผู้ใช้ลงชื่อเข้าใช้โดยใช้รหัสผ่าน
  • เมื่อผู้ใช้ลงชื่อเข้าใช้โดยใช้พาสคีย์จากอุปกรณ์เครื่องอื่น (นั่นคือ authenticatorAttachment เท่ากับ cross-platform)
  • ในหน้าเฉพาะที่ผู้ใช้จัดการพาสคีย์ได้

หากต้องการสร้างพาสคีย์ คุณต้องใช้ WebAuthn API

ขั้นตอนการลงทะเบียนพาสคีย์มีองค์ประกอบ 4 อย่างดังนี้

  • แบ็กเอนด์: เซิร์ฟเวอร์แบ็กเอนด์ที่มีฐานข้อมูลบัญชีที่จัดเก็บคีย์สาธารณะและข้อมูลเมตาอื่นๆ เกี่ยวกับพาสคีย์
  • หน้าเว็บ: หน้าเว็บที่สื่อสารกับเบราว์เซอร์และส่งคำขอดึงข้อมูลไปยังแบ็กเอนด์
  • เบราว์เซอร์: เบราว์เซอร์ของผู้ใช้ที่เรียกใช้ JavaScript
  • โปรแกรมตรวจสอบสิทธิ์: โปรแกรมตรวจสอบสิทธิ์ของผู้ใช้ที่สร้างและจัดเก็บพาสคีย์ ซึ่งอาจรวมถึงเครื่องมือจัดการรหัสผ่านในอุปกรณ์เดียวกับเบราว์เซอร์ (เช่น เมื่อใช้ Windows Hello) หรือในอุปกรณ์อื่น เช่น โทรศัพท์
แผนภาพการลงทะเบียนพาสคีย์

ขั้นตอนในการเพิ่มพาสคีย์ใหม่ลงในบัญชีผู้ใช้ที่มีอยู่มีดังนี้

  1. ผู้ใช้ลงชื่อเข้าใช้เว็บไซต์
  2. เมื่อลงชื่อเข้าใช้แล้ว ผู้ใช้จะสร้างพาสคีย์ในหน้าเว็บได้ เช่น โดยการกดปุ่ม "สร้างพาสคีย์"
  3. ฟรอนท์เอนด์จะขอข้อมูลจากแบ็กเอนด์เพื่อสร้างพาสคีย์ เช่น ข้อมูลผู้ใช้ คำถาม และรหัสข้อมูลเข้าสู่ระบบที่จะยกเว้น
  4. หน้าเว็บเรียก navigator.credentials.create() เพื่อสร้างพาสคีย์ การเรียกนี้จะแสดงผลลัพธ์เป็นสัญญา
  5. ระบบจะสร้างพาสคีย์หลังจากที่ผู้ใช้ให้ความยินยอมโดยใช้ล็อกหน้าจอของอุปกรณ์ สัญญาจะได้รับการแก้ไขและระบบจะส่งข้อมูลเข้าสู่ระบบคีย์สาธารณะไปยังฟรอนท์เอนด์
  6. ฟีดด้านหน้าจะส่งข้อมูลเข้าสู่ระบบคีย์สาธารณะไปยังแบ็กเอนด์และจัดเก็บรหัสข้อมูลเข้าสู่ระบบและคีย์สาธารณะที่เชื่อมโยงกับบัญชีผู้ใช้สำหรับการตรวจสอบสิทธิ์ในอนาคต

ความเข้ากันได้

เบราว์เซอร์ส่วนใหญ่รองรับ WebAuthn อยู่ แต่ก็ยังมีช่องโหว่เล็กน้อย โปรดดูหัวข้อการรองรับอุปกรณ์ - passkeys.dev เพื่อดูว่าเบราว์เซอร์และระบบปฏิบัติการใดบ้างที่รองรับการสร้างพาสคีย์

สร้างพาสคีย์ใหม่

ต่อไปนี้เป็นวิธีที่ส่วนหน้าควรทำงานเมื่อได้รับคำขอสร้างพาสคีย์ใหม่

การตรวจหาองค์ประกอบ

ก่อนแสดงปุ่ม "สร้างพาสคีย์ใหม่" ให้ตรวจสอบว่า

  • เบราว์เซอร์รองรับ WebAuthn ด้วย PublicKeyCredential

การรองรับเบราว์เซอร์

  • Chrome: 67
  • ขอบ: 18
  • Firefox: 60
  • Safari: 13.

แหล่งที่มา

  • อุปกรณ์รองรับโปรแกรมตรวจสอบสิทธิ์ของแพลตฟอร์ม (สร้างพาสคีย์และตรวจสอบสิทธิ์ด้วยพาสคีย์ได้) ด้วย PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()

การรองรับเบราว์เซอร์

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

แหล่งที่มา

  • เบราว์เซอร์รองรับ WebAuthn conditional UI ด้วย PublicKeyCredenital.isConditionalMediationAvailable()

การรองรับเบราว์เซอร์

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

แหล่งที่มา

// 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 จะแสดงผลเป็น Promise ซึ่งรอการโต้ตอบของผู้ใช้โดยแสดงกล่องโต้ตอบแบบโมดอล

การรองรับเบราว์เซอร์

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

แหล่งที่มา

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 คือโดเมน และเว็บไซต์จะระบุโดเมนหรือส่วนต่อท้ายที่จดทะเบียนได้ เช่น หากต้นทางของ RP คือ https://login.example.com:1337 รหัส RP อาจเป็น login.example.com หรือ example.com หากระบุรหัส RP เป็น example.com ผู้ใช้จะตรวจสอบสิทธิ์ใน login.example.com หรือในโดเมนย่อยใดก็ได้ของ example.com

  • rp.name: ชื่อของ RP

  • pubKeyCredParams: ช่องนี้ระบุอัลกอริทึมคีย์สาธารณะที่ RP รองรับ เราขอแนะนำให้ ตั้งค่าเป็น [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}] ซึ่งระบุการรองรับ ECDSA ด้วย P-256 และ RSA PKCS#1 และการรองรับเหล่านี้จะทำให้ครอบคลุมอย่างสมบูรณ์

  • authenticatorSelection.authenticatorAttachment: ตั้งค่านี้เป็น "platform" หากการสร้างพาสคีย์นี้เป็นการอัปเกรดจากรหัสผ่าน เช่น ในโปรโมชันหลังจากลงชื่อเข้าใช้ "platform" ระบุว่า RP ต้องการโปรแกรมตรวจสอบสิทธิ์ของแพลตฟอร์ม (โปรแกรมตรวจสอบสิทธิ์ที่ฝังอยู่ในอุปกรณ์แพลตฟอร์ม) ซึ่งจะไม่แจ้งให้เสียบ เช่น คีย์ความปลอดภัยแบบ USB ผู้ใช้มีตัวเลือกที่ง่ายกว่าในการสร้างพาสคีย์

  • authenticatorSelection.requireResidentKey: ตั้งค่าเป็นบูลีน "จริง" ข้อมูลเข้าสู่ระบบที่ค้นพบได้ (คีย์ที่อยู่ในเครื่อง) จะจัดเก็บข้อมูลผู้ใช้ไว้ในพาสคีย์และอนุญาตให้ผู้ใช้เลือกบัญชีเมื่อตรวจสอบสิทธิ์ ดูข้อมูลเพิ่มเติมเกี่ยวกับข้อมูลเข้าสู่ระบบที่ค้นพบได้ที่หัวข้อเจาะลึกข้อมูลเข้าสู่ระบบที่ค้นพบได้

  • authenticatorSelection.userVerification: ระบุว่าการยืนยันผู้ใช้โดยใช้การล็อกหน้าจอของอุปกรณ์เป็น"required", "preferred" หรือ "discouraged" ค่าเริ่มต้นคือ "preferred" ซึ่งหมายความว่าโปรแกรมตรวจสอบสิทธิ์อาจข้ามการยืนยันผู้ใช้ ตั้งค่าเป็น "preferred" หรือละเว้นพร็อพเพอร์ตี้นี้

ส่งข้อมูลเข้าสู่ระบบคีย์สาธารณะที่ส่งคืนไปยังแบ็กเอนด์

หลังจากที่ผู้ใช้ยินยอมโดยใช้การล็อกหน้าจอของอุปกรณ์ ระบบจะสร้างพาสคีย์และแก้ปัญหาสัญญาสำเร็จโดยส่งออบเจ็กต์ PublicKeyCredential ไปยังฟรอนท์เอนด์

การปฏิเสธคำมั่นสัญญาอาจเกิดขึ้นได้จากหลายสาเหตุ คุณจัดการข้อผิดพลาดเหล่านี้ได้โดยตรวจสอบพร็อพเพอร์ตี้ name ของออบเจ็กต์ Error ดังนี้

  • InvalidStateError: มีพาสคีย์อยู่ในอุปกรณ์อยู่แล้ว ระบบจะไม่แสดงกล่องโต้ตอบข้อผิดพลาดให้ผู้ใช้เห็นและเว็บไซต์ไม่ควรถือว่านี่เป็นข้อผิดพลาด เนื่องจากผู้ใช้ต้องการให้มีการลงทะเบียนอุปกรณ์ในระบบและเป็นเช่นนั้น
  • NotAllowedError: ผู้ใช้ยกเลิกการดำเนินการ
  • ข้อยกเว้นอื่นๆ: เกิดข้อผิดพลาดที่ไม่คาดคิด เบราว์เซอร์จะแสดงกล่องโต้ตอบแสดงข้อผิดพลาดให้ผู้ใช้เห็น

ออบเจ็กต์ข้อมูลเข้าสู่ระบบด้วยคีย์สาธารณะมีพร็อพเพอร์ตี้ต่อไปนี้

  • id: รหัสที่เข้ารหัส Base64URL ของพาสคีย์ที่สร้างขึ้น รหัสนี้ช่วยให้เบราว์เซอร์ทราบว่ามีพาสคีย์ที่ตรงกันในอุปกรณ์หรือไม่เมื่อตรวจสอบสิทธิ์ ต้องเก็บค่านี้ไว้ในฐานข้อมูลในแบ็กเอนด์
  • rawId: รหัสข้อมูลเข้าสู่ระบบเวอร์ชัน ArrayBuffer
  • response.clientDataJSON: ข้อมูลไคลเอ็นต์ที่เข้ารหัส ArrayBuffer
  • response.attestationObject: ออบเจ็กต์เอกสารรับรองที่เข้ารหัส ArrayBuffer ซึ่งประกอบด้วยข้อมูลสำคัญ เช่น รหัส RP, Flag และคีย์สาธารณะ
  • authenticatorAttachment: แสดงผล "platform" เมื่อสร้างข้อมูลเข้าสู่ระบบนี้ในอุปกรณ์ที่ใช้พาสคีย์ได้
  • type: ฟิลด์นี้จะตั้งค่าเป็น "public-key" เสมอ

หากคุณใช้ไลบรารีในการจัดการออบเจ็กต์ข้อมูลเข้าสู่ระบบคีย์สาธารณะในแบ็กเอนด์ เราขอแนะนำให้ส่งออบเจ็กต์ทั้งหมดไปยังแบ็กเอนด์หลังจากเข้ารหัสออบเจ็กต์บางส่วนด้วย base64url

บันทึกข้อมูลเข้าสู่ระบบ

เมื่อได้รับข้อมูลเข้าสู่ระบบคีย์สาธารณะในแบ็กเอนด์ ให้ส่งข้อมูลดังกล่าวไปยังไลบรารี FIDO เพื่อประมวลผลออบเจ็กต์

จากนั้นคุณสามารถจัดเก็บข้อมูลที่ดึงมาจากข้อมูลเข้าสู่ระบบไว้ในฐานข้อมูลเพื่อใช้ในอนาคต รายการต่อไปนี้มีพร็อพเพอร์ตี้ทั่วไปที่ควรบันทึก

  • รหัสเข้าสู่ระบบ (คีย์หลัก)
  • User ID
  • คีย์สาธารณะ

ข้อมูลเข้าสู่ระบบด้วยคีย์สาธารณะยังมีข้อมูลต่อไปนี้ด้วยซึ่งคุณอาจต้องการบันทึกไว้ในฐานข้อมูล

ทำตามวิธีการโดยละเอียดที่การลงทะเบียนพาสคีย์ฝั่งเซิร์ฟเวอร์

หากต้องการตรวจสอบสิทธิ์ผู้ใช้ โปรดอ่านลงชื่อเข้าใช้ด้วยพาสคีย์ผ่านการป้อนข้อความอัตโนมัติของแบบฟอร์ม

แหล่งข้อมูล