Khoá truy cập giúp tài khoản người dùng an toàn, đơn giản và dễ sử dụng hơn.
Xuất bản: Ngày 12 tháng 10 năm 2022, Cập nhật lần gần đây nhất: Ngày 9 tháng 4 năm 2026
Việc sử dụng khoá truy cập giúp tăng cường bảo mật, đơn giản hoá quy trình đăng nhập và thay thế mật khẩu. Không giống như mật khẩu thông thường mà người dùng phải nhớ và nhập theo cách thủ công, khoá truy cập sử dụng phương thức khoá màn hình của thiết bị (chẳng hạn như dữ liệu sinh trắc học hoặc mã PIN) và giảm nguy cơ tấn công giả mạo cũng như đánh cắp thông tin đăng nhập.
Khoá truy cập đồng bộ hoá trên các thiết bị bằng cách sử dụng các nhà cung cấp khoá truy cập như Trình quản lý mật khẩu của Google và Chuỗi khoá iCloud.
Bạn phải tạo một khoá truy cập, lưu trữ khoá riêng tư một cách an toàn cho nhà cung cấp khoá truy cập cùng với siêu dữ liệu cần thiết và khoá công khai của khoá truy cập được lưu trữ trên máy chủ của bạn để xác thực. Khoá riêng tư sẽ phát hành chữ ký sau khi xác minh người dùng trên miền hợp lệ, giúp khoá truy cập chống được hành vi lừa đảo. Khoá công khai xác minh chữ ký mà không lưu trữ thông tin đăng nhập nhạy cảm, giúp mã khoá chống lại hành vi trộm cắp thông tin đăng nhập.
Cách hoạt động của việc tạo khoá truy cập
Trước khi người dùng có thể đăng nhập bằng khoá truy cập, bạn nên tạo khoá truy cập, liên kết khoá đó với một tài khoản người dùng và lưu trữ khoá công khai của khoá truy cập đó trên máy chủ của bạn.
Bạn có thể yêu cầu người dùng tạo khoá truy cập trong một trong các trường hợp sau:
- Trong hoặc sau khi đăng ký.
- Sau khi đăng nhập.
- Sau khi đăng nhập bằng khoá truy cập trên một thiết bị khác (tức là
authenticatorAttachmentcross-platform). - Trên một trang chuyên dụng nơi người dùng có thể quản lý khoá truy cập của họ.
Để tạo khoá truy cập, bạn sử dụng WebAuthn API.
Quy trình đăng ký khoá truy cập có 4 thành phần:
- Phụ trợ: Lưu trữ thông tin chi tiết về tài khoản người dùng, bao gồm cả khoá công khai.
- Giao diện người dùng: Giao tiếp với trình duyệt và tìm nạp dữ liệu cần thiết từ phần phụ trợ.
- Trình duyệt: Chạy JavaScript và tương tác với WebAuthn API.
- Nhà cung cấp khoá truy cập: Tạo và lưu trữ khoá truy cập. Đây thường là một trình quản lý mật khẩu (chẳng hạn như Trình quản lý mật khẩu của Google) hoặc một khoá bảo mật.
Trước khi tạo khoá truy cập, hãy đảm bảo hệ thống đáp ứng các điều kiện tiên quyết sau:
Tài khoản người dùng được xác minh thông qua một phương thức bảo mật (ví dụ: xác minh qua email, (quy trình) xác minh qua điện thoại hoặc liên kết danh tính) trong một khoảng thời gian ngắn có ý nghĩa.
Giao diện người dùng và phần phụ trợ có thể giao tiếp một cách an toàn để trao đổi dữ liệu thông tin đăng nhập.
Trình duyệt này hỗ trợ WebAuthn và việc tạo khoá truy cập.
Chúng tôi có thể hướng dẫn bạn cách kiểm tra hầu hết các chỉ số trong các phần sau.
Sau khi hệ thống đáp ứng các điều kiện này, quy trình sau sẽ diễn ra để tạo khoá truy cập:
- Hệ thống sẽ kích hoạt quy trình tạo khoá truy cập khi người dùng bắt đầu hành động (ví dụ: nhấp vào nút "Tạo khoá truy cập" trong trang quản lý khoá truy cập hoặc sau khi hoàn tất quy trình đăng ký).
- Giao diện người dùng yêu cầu dữ liệu thông tin đăng nhập cần thiết từ phần phụ trợ, bao gồm thông tin người dùng, một thử thách và mã nhận dạng thông tin đăng nhập để ngăn chặn các thông tin đăng nhập trùng lặp.
- Giao diện người dùng gọi
navigator.credentials.create()để nhắc trình cung cấp khoá truy cập của thiết bị tạo khoá truy cập bằng thông tin từ phần phụ trợ. Xin lưu ý rằng lệnh gọi này trả về một promise. - Thiết bị của người dùng xác thực người dùng bằng phương thức sinh trắc học, mã PIN hoặc hình mở khoá để tạo khoá truy cập.
- Trình cung cấp khoá truy cập sẽ tạo một khoá truy cập và trả về một thông tin xác thực khoá công khai cho giao diện người dùng, giải quyết lời hứa.
- Giao diện người dùng gửi thông tin xác thực khoá công khai đã tạo đến phần phụ trợ.
- Phần phụ trợ lưu trữ khoá công khai và các dữ liệu quan trọng khác để xác thực sau này,
- Phần phụ trợ thông báo cho người dùng (ví dụ: bằng email) để xác nhận việc tạo khoá truy cập và phát hiện hành vi truy cập trái phép tiềm ẩn.
Quy trình này đảm bảo quy trình đăng ký khoá truy cập an toàn và liền mạch cho người dùng.
Khả năng tương thích
Hầu hết các trình duyệt đều hỗ trợ WebAuthn, chỉ có một số điểm thiếu sót nhỏ. Hãy xem passkeys.dev để biết thông tin chi tiết về khả năng tương thích của trình duyệt và hệ điều hành.
Tạo khoá truy cập mới
Để tạo khoá truy cập mới, đây là quy trình mà giao diện người dùng nên tuân theo:
- Kiểm tra khả năng tương thích.
- Tìm nạp thông tin từ phần phụ trợ.
- Gọi API WebAuth để tạo khoá truy cập.
- Gửi khoá công khai đã trả về đến phần phụ trợ.
- Lưu thông tin đăng nhập.
Các phần sau đây cho biết cách bạn có thể thực hiện việc này.
Kiểm tra khả năng tương thích
Trước khi hiển thị nút "Tạo khoá truy cập mới", giao diện người dùng nên kiểm tra xem:
- Trình duyệt hỗ trợ WebAuthn bằng
PublicKeyCredential.
- Trình duyệt hỗ trợ khả năng phát hiện bằng
PublicKeyCredential.getClientCapabilities().
Trình duyệt hỗ trợ Giao diện người dùng có điều kiện WebAuthn bằng
conditionalGet.Thiết bị hỗ trợ một trình xác thực nền tảng (có thể tạo khoá truy cập và xác thực trên thiết bị) bằng
passkeyPlatformAuthenticator.
Đoạn mã sau đây cho biết cách bạn có thể kiểm tra khả năng tương thích trước khi hiển thị các lựa chọn liên quan đến khoá truy cập.
if (window.PublicKeyCredential && PublicKeyCredential.getClientCapabilities) {
const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalGet === true &&
capabilities.passkeyPlatformAuthenticator === true) {
// The browser supports passkeys and the conditional UI.
}
}
Trong ví dụ này, nút Tạo khoá truy cập mới chỉ được hiển thị nếu tất cả các điều kiện đều được đáp ứng.
Tìm nạp thông tin từ phần phụ trợ
Khi người dùng nhấp vào nút, hãy tìm nạp thông tin cần thiết từ phần phụ trợ để gọi navigator.credentials.create().
Đoạn mã sau đây minh hoạ một đối tượng JSON có thông tin bắt buộc để gọi navigator.credentials.create():
// Example `PublicKeyCredentialCreationOptions` contents
{
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,
}
}
Các cặp khoá-giá trị trong đối tượng này chứa thông tin sau:
challenge: Một thử thách do máy chủ tạo trong ArrayBuffer cho quá trình đăng ký này.rp.id: RP ID (Mã nhận dạng Bên tin cậy), một miền và một trang web có thể chỉ định miền hoặc hậu tố có thể đăng ký. Ví dụ: nếu nguồn của RP làhttps://login.example.com:1337, thì RP ID có thể làlogin.example.comhoặcexample.com. Nếu RP ID được chỉ định làexample.com, thì người dùng có thể xác thực trênlogin.example.comhoặc trên bất kỳ miền con nào trênexample.com. Hãy xem phần Cho phép sử dụng lại khoá truy cập trên các trang web của bạn bằng Yêu cầu về nguồn gốc có liên quan để biết thêm thông tin về vấn đề này.rp.name: Tên của RP (Bên tin cậy). Tham số này không được dùng trong WebAuthn L3 nhưng vẫn được đưa vào vì lý do tương thích.user.id: Mã nhận dạng người dùng duy nhất trong ArrayBuffer, được tạo khi tạo tài khoản. Mã này phải là mã cố định, không giống như tên người dùng có thể chỉnh sửa. Mã nhận dạng người dùng xác định một tài khoản, nhưng không được chứa thông tin nhận dạng cá nhân (PII). Có thể bạn đã có mã nhận dạng người dùng trong hệ thống của mình, nhưng nếu cần, hãy tạo một mã nhận dạng người dùng dành riêng cho khoá truy cập để đảm bảo mã này không chứa thông tin nhận dạng cá nhân.user.name: Giá trị nhận dạng duy nhất cho tài khoản mà người dùng sẽ nhận ra, chẳng hạn như địa chỉ email hoặc tên người dùng của họ. Thông tin này sẽ xuất hiện trong bộ chọn tài khoản.user.displayName: Tên bắt buộc và thân thiện hơn với người dùng cho tài khoản. Không cần phải là duy nhất và có thể là tên mà người dùng đã chọn. Nếu trang web của bạn không có giá trị phù hợp để đưa vào đây, hãy truyền một chuỗi trống. Thông tin này có thể xuất hiện trên bộ chọn tài khoản, tuỳ thuộc vào trình duyệt.pubKeyCredParams: Chỉ định các thuật toán khoá công khai được RP (bên đáng tin cậy) hỗ trợ. Bạn nên đặt giá trị này thành[{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]. Điều này chỉ định khả năng hỗ trợ ECDSA bằng P-256 và RSA PKCS#1, đồng thời việc hỗ trợ các thuật toán này sẽ giúp bạn có được phạm vi hỗ trợ đầy đủ.excludeCredentials: Một danh sách các mã nhận dạng thông tin đăng nhập đã đăng ký. Ngăn việc đăng ký cùng một thiết bị hai lần bằng cách cung cấp danh sách mã thông tin xác thực đã đăng ký. Thành phầntransports(nếu được cung cấp) sẽ chứa kết quả của lệnh gọigetTransports()trong quá trình đăng ký từng thông tin đăng nhập.authenticatorSelection.authenticatorAttachment: Đặt giá trị này thành"platform"cùng vớihint: ['client-device']nếu việc tạo khoá truy cập này là một bản nâng cấp từ mật khẩu, chẳng hạn như trong một chương trình khuyến mãi sau khi đăng nhập."platform"cho biết RP muốn một trình xác thực nền tảng (một trình xác thực được nhúng vào thiết bị nền tảng) không nhắc người dùng, chẳng hạn như nhắc người dùng gắn khoá bảo mật USB. Người dùng có một lựa chọn đơn giản hơn để tạo khoá truy cập.authenticatorSelection.requireResidentKey: Đặt thành giá trị booleantrue. Thông tin đăng nhập có thể phát hiện (khoá cư trú) lưu trữ thông tin người dùng vào khoá truy cập và cho phép người dùng chọn tài khoản khi xác thực.authenticatorSelection.userVerification: Cho biết liệu quy trình xác minh người dùng bằng tính năng khoá màn hình thiết bị là"required","preferred"hay"discouraged". Giá trị mặc định là"preferred", tức là trình xác thực có thể bỏ qua bước xác minh người dùng. Đặt thuộc tính này thành"preferred"hoặc bỏ qua thuộc tính.
Bạn nên tạo đối tượng trên máy chủ, mã hoá ArrayBuffer bằng Base64URL và tìm nạp đối tượng đó từ giao diện người dùng. Bằng cách này, bạn có thể giải mã tải trọng bằng PublicKeyCredential.parseCreationOptionsFromJSON() và truyền trực tiếp tải trọng đó đến navigator.credentials.create().
Đoạn mã sau đây cho biết cách bạn có thể tìm nạp và giải mã thông tin cần thiết để tạo khoá truy cập.
// Fetch an encoded `PubicKeyCredentialCreationOptions` from the server.
const _options = await fetch('/webauthn/registerRequest');
// Deserialize and decode the `PublicKeyCredentialCreationOptions`.
const decoded_options = JSON.parse(_options);
const options = PublicKeyCredential.parseCreationOptionsFromJSON(decoded_options);
...
Gọi API WebAuthn để tạo khoá truy cập
Gọi navigator.credentials.create() để tạo khoá truy cập mới. API này trả về một lời hứa, chờ đợi hoạt động tương tác của người dùng khi hiển thị một hộp thoại phương thức.
// Invoke WebAuthn to create a passkey.
const credential = await navigator.credentials.create({
publicKey: options
});
Gửi thông tin xác thực khoá công khai được trả về đến phần phụ trợ
Sau khi người dùng được xác minh bằng khoá màn hình của thiết bị, một khoá truy cập sẽ được tạo và lời hứa được giải quyết bằng cách trả về một đối tượng PublicKeyCredential cho giao diện người dùng.
Lời hứa có thể bị từ chối vì nhiều lý do. Bạn có thể xử lý các lỗi này bằng cách kiểm tra thuộc tính name của đối tượng Error:
InvalidStateError: Đã có khoá truy cập trên thiết bị. Người dùng sẽ không thấy hộp thoại lỗi. Trang web không được coi đây là một lỗi. Người dùng muốn đăng ký thiết bị cục bộ và thiết bị đó đã được đăng ký.NotAllowedError: Người dùng đã huỷ thao tác.AbortError: Thao tác đã bị huỷ.- Các trường hợp ngoại lệ khác: Đã xảy ra lỗi không mong muốn. Trình duyệt sẽ hiển thị hộp thoại lỗi cho người dùng.
Đối tượng thông tin đăng nhập khoá công khai chứa các thuộc tính sau:
id: Mã nhận dạng được mã hoá Base64URL của khoá truy cập đã tạo. Trong quá trình xác thực thì mã nhận dạng này sẽ giúp trình duyệt xác định xem thiết bị có khoá truy cập phù hợp hay không. Giá trị này phải được lưu trữ trong cơ sở dữ liệu ở máy chủ phụ trợ.rawId: Một phiên bản ArrayBuffer của mã nhận dạng thông tin đăng nhập.response.clientDataJSON: Dữ liệu ứng dụng khách được mã hoá ArrayBuffer.response.attestationObject: Một đối tượng chứng thực được mã hoá ArrayBuffer. Đối tượng này có chứa thông tin quan trọng, chẳng hạn như mã nhận dạng RP, cờ và khoá công khai.authenticatorAttachment: Trả về"platform"khi thông tin xác thực này được tạo trên một thiết bị có thể dùng khoá truy cập.type: Trường này luôn được đặt thành"public-key".
Mã hoá đối tượng bằng phương thức .toJSON(), chuyển đổi đối tượng thành chuỗi bằng JSON.stringify() rồi gửi đến máy chủ.
...
// Encode and serialize the `PublicKeyCredential`.
const _result = credential.toJSON();
const result = JSON.stringify(_result);
// Encode and send the credential to the server for verification.
const response = await fetch('/webauthn/registerResponse', {
method: 'post',
credentials: 'same-origin',
body: result
});
...
Lưu thông tin đăng nhập
Sau khi nhận được thông tin đăng nhập bằng khoá công khai trên phần phụ trợ, bạn nên sử dụng một thư viện phía máy chủ hoặc một giải pháp thay vì tự viết mã để xử lý thông tin đăng nhập bằng khoá công khai.
Sau đó, bạn có thể lưu trữ thông tin đã truy xuất từ thông tin đăng nhập vào cơ sở dữ liệu để sử dụng sau này.
Danh sách sau đây bao gồm các thuộc tính nên lưu:
- Mã thông tin xác thực: Mã thông tin xác thực được trả về cùng với thông tin xác thực khoá công khai.
- Tên chứng chỉ: Tên của chứng chỉ. Đặt tên cho khoá truy cập theo nhà cung cấp khoá truy cập đã tạo khoá truy cập đó. Bạn có thể xác định nhà cung cấp dựa trên AAGUID.
- Mã nhận dạng người dùng: Mã nhận dạng người dùng được dùng để tạo khoá truy cập.
- Khoá công khai: Khoá công khai được trả về cùng với thông tin xác thực khoá công khai. Bạn cần phải xác minh một câu xác nhận khoá truy cập.
- Ngày và giờ tạo: Ghi lại ngày và giờ tạo khoá truy cập. Điều này hữu ích để xác định khoá truy cập.
- Ngày và giờ sử dụng gần đây nhất: Ghi lại ngày và giờ gần đây nhất mà người dùng sử dụng khoá truy cập để đăng nhập. Điều này hữu ích để xác định khoá truy cập mà người dùng đã sử dụng (hoặc chưa sử dụng).
- AAGUID: Giá trị nhận dạng riêng biệt của nhà cung cấp khoá truy cập.
- Cờ đủ điều kiện sao lưu: true nếu thiết bị đủ điều kiện đồng bộ hoá khoá truy cập. Thông tin này giúp người dùng xác định khoá truy cập có thể đồng bộ hoá và khoá truy cập chỉ dùng được trên thiết bị (không đồng bộ hoá được) trên trang quản lý khoá truy cập.
Làm theo hướng dẫn chi tiết hơn tại Đăng ký khoá truy cập phía máy chủ
Báo hiệu nếu không đăng ký được
Nếu quá trình đăng ký khoá truy cập không thành công, người dùng có thể bị nhầm lẫn. Nếu có một khoá truy cập trong trình cung cấp khoá truy cập và người dùng có thể sử dụng khoá truy cập đó, nhưng khoá công khai được liên kết không được lưu trữ ở phía máy chủ, thì các lần đăng nhập bằng khoá truy cập sẽ không bao giờ thành công và rất khó khắc phục sự cố. Nhớ thông báo cho người dùng nếu trường hợp này xảy ra.
Để ngăn chặn trường hợp như vậy, bạn có thể báo hiệu một khoá truy cập không xác định cho trình cung cấp khoá truy cập bằng Signal API.
Bằng cách gọi PublicKeyCredential.signalUnknownCredential() bằng mã nhận dạng RP và mã nhận dạng thông tin đăng nhập, RP có thể thông báo cho nhà cung cấp khoá truy cập rằng thông tin đăng nhập được chỉ định đã bị xoá hoặc không tồn tại. Nhà cung cấp khoá truy cập sẽ quyết định cách xử lý tín hiệu này. Tuy nhiên, nếu được hỗ trợ, khoá truy cập liên kết dự kiến sẽ bị xoá.
// Detect authentication failure due to lack of the credential
if (response.status === 404) {
// Feature detection
if (PublicKeyCredential.signalUnknownCredential) {
await PublicKeyCredential.signalUnknownCredential({
rpId: "example.com",
credentialId: "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA" // base64url encoded credential ID
});
} else {
// Encourage the user to delete the passkey from the password manager nevertheless.
...
}
}
Để tìm hiểu thêm về Signal API, hãy đọc bài viết Duy trì tính nhất quán giữa khoá truy cập và thông tin xác thực trên máy chủ của bạn bằng Signal API.
Gửi thông báo cho người dùng
Việc gửi thông báo (chẳng hạn như email) khi khoá truy cập được đăng ký sẽ giúp người dùng phát hiện hành vi truy cập trái phép vào tài khoản. Nếu kẻ tấn công tạo một khoá truy cập mà người dùng không biết, thì khoá truy cập đó vẫn có thể bị lợi dụng trong tương lai, ngay cả sau khi mật khẩu bị thay đổi. Thông báo này sẽ cảnh báo người dùng và giúp ngăn chặn tình trạng này.
Danh sách kiểm tra
- Xác minh người dùng (tốt nhất là dùng email hoặc một phương thức bảo mật) trước khi cho phép họ tạo khoá truy cập.
- Ngăn việc tạo khoá truy cập trùng lặp cho cùng một nhà cung cấp khoá truy cập bằng cách sử dụng
excludeCredentials. - Lưu AAGUID để xác định nhà cung cấp khoá truy cập và đặt tên cho thông tin đăng nhập của người dùng.
- Đưa ra tín hiệu nếu một lần thử đăng ký khoá truy cập không thành công với
PublicKeyCredential.signalUnknownCredential(). - Gửi thông báo cho người dùng sau khi tạo và đăng ký khoá truy cập cho tài khoản của họ.
Tài nguyên
- Đăng ký khoá truy cập phía máy chủ
- Tài liệu của Apple: Xác thực người dùng thông qua dịch vụ web
- Tài liệu của Google: Đăng nhập không cần mật khẩu bằng khoá truy cập
Bước tiếp theo: Đăng nhập bằng khoá truy cập thông qua tính năng tự động điền biểu mẫu.