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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ฟรอนท์เอนด์ควรดำเนินการต่อไปนี้ตามคำขอให้สร้างพาสคีย์ใหม่

การตรวจหาฟีเจอร์

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

  • เบราว์เซอร์นี้รองรับ WebAuthn
  • อุปกรณ์รองรับตัวตรวจสอบสิทธิ์แพลตฟอร์ม (สร้างพาสคีย์และตรวจสอบสิทธิ์ด้วยพาสคีย์ได้)
  • เบราว์เซอร์รองรับ UI แบบมีเงื่อนไขของ 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 คือโดเมนและเว็บไซต์จะระบุโดเมนหรือคำต่อท้ายที่จดทะเบียนได้ เช่น หากต้นทางของ 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 ต้องการ Authenticator สำหรับแพลตฟอร์ม (Authenticator ที่ฝังอยู่ในอุปกรณ์แพลตฟอร์ม) ซึ่งจะไม่แสดงข้อความแจ้งให้เสียบ เช่น คีย์ความปลอดภัย USB ผู้ใช้มีตัวเลือกที่ง่ายกว่าในการสร้างพาสคีย์

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

แหล่งข้อมูล