Hai kiểm tra WebAuthn không cần quyền dùng để bật đăng nhập passkey còn lộ dòng thiết bị, hệ điều hành, engine trình duyệt — tín hiệu vân tay yếu mà đáng chú ý.
Tháng 8/2026, blog Android Developers của Google đã kể lại cách WhatsApp chuyển sang khóa truy cập (passkey) cho 1 tỷ người dùng — bằng chứng cho thấy passkey đã rời khỏi nhóm người dùng tiên phong để trở thành lựa chọn mặc định đại trà. WhatsApp là ứng dụng Android gốc, nhưng sự chuyển dịch tương tự cũng đang diễn ra trên web, nơi trang đăng nhập phải hỏi trình duyệt "thiết bị này có dùng được passkey không?" trước khi hiển thị tùy chọn đó. Bài viết này bàn về việc câu trả lời cho câu hỏi ấy tiết lộ điều gì — với trang web đặt câu hỏi, và với bất kỳ script nào khác đang chạy trên cùng trang.
Điểm chính
- Hai kiểm tra khả năng quyết định có hiện giao diện passkey hay không, và cả hai đều không cần cấp quyền.
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()vàisConditionalMediationAvailable()là các kiểm tra trả về Promise, không có hộp thoại xin quyền, không cần thao tác của người dùng và không hiện giao diện nào — xem đặc tả WebAuthn Level 3 và tài liệu tham khảo của MDN. - Câu trả lời đúng/sai tương quan với dòng thiết bị, phiên bản hệ điều hành tối thiểu và engine trình duyệt. Trình xác thực nền tảng nghĩa là cơ chế xác minh người dùng tích hợp sẵn — Face ID, Touch ID, Windows Hello hoặc khóa màn hình Android — gắn với những phiên bản hệ điều hành và engine cụ thể, nên chỉ một giá trị boolean cũng phân loại người truy cập rõ nét hơn bạn tưởng.
- Thành thật mà nói, entropy ở đây thấp. Ngay cả khi tính thêm gói thông tin mới hơn từ
getClientCapabilities(), các câu trả lời phần lớn vẫn trùng với những gì User-Agent đã tiết lộ, nên tự chúng gần như không thu hẹp được danh tính của ai. - Giá trị thực sự của nó là kiểm tra mâu thuẫn, không phải định danh. Một User-Agent tự nhận là điện thoại flagship đời mới nhưng lại không có trình xác thực nền tảng mới là trường hợp đáng chú ý — hồ sơ điển hình của trình duyệt chống phát hiện (anti-detect) hoặc trình giả lập.
- Kiểm tra này chỉ tiết lộ khả năng. Nó không thể cho script biết bạn có passkey cho trang này hay bất kỳ trang nào, càng không biết bạn là ai — điều đó đòi hỏi một quy trình tạo/dùng thông tin xác thực rõ ràng, có xác minh người dùng và chỉ gắn với một bên phụ thuộc (relying party).
Hai kiểm tra đứng sau mọi nút "Dùng passkey?"
Trang đăng nhập muốn đưa ra tùy chọn passkey thì trước hết phải biết thiết bị có tạo được passkey hay không. WebAuthn cung cấp hai phương thức tĩnh trên PublicKeyCredential, đều được định nghĩa trong đặc tả WebAuthn Level 3 do W3C duy trì:
isUserVerifyingPlatformAuthenticatorAvailable()trả lời đúng một câu hỏi: trình duyệt này, trên thiết bị này, có dùng được một trình xác thực tích hợp sẵn có khả năng xác minh người dùng hay không — Face ID hoặc Touch ID, Windows Hello (khuôn mặt, vân tay hoặc mã PIN), hay bước xác minh sinh trắc học hoặc khóa màn hình trên thiết bị Android? MDN mô tả đây là phương thức tĩnh trả vềPromise<boolean>.isConditionalMediationAvailable()trả lời một câu hỏi hẹp hơn, có liên quan: trình duyệt có thể hiện gợi ý tự động điền passkey ngay trong ô tên đăng nhập thông thường, không cần hộp thoại modal chen ngang trang hay không?
Các trình duyệt mới hơn còn bổ sung phương thức thứ ba, bao quát hơn: PublicKeyCredential.getClientCapabilities(), trả về cả một bản ghi các giá trị boolean chỉ trong một lần gọi — conditional get và create, phương thức truyền hybrid (liên thiết bị), hỗ trợ trình xác thực nền tảng cho passkey, Related Origin Requests, các phương thức "signal" mới hơn và những tiện ích mở rộng được hỗ trợ. Giống hai kiểm tra cũ, nó chỉ cần ngữ cảnh bảo mật (HTTPS).
Cả hai đều có chung ba đặc điểm quan trọng với bài viết này: không cần hộp thoại xin quyền, không cần thao tác của người dùng và không tự tạo ra giao diện nào nhìn thấy được. Trang có thể gọi một trong hai ngay khi vừa tải, nhận câu trả lời trong vài mili giây, và người truy cập không hề thấy gì xảy ra. Đó là chủ đích thiết kế — trang đăng nhập vốn phải âm thầm quyết định xem có nên hiện nút "Đăng nhập bằng passkey" hay không. Nhưng chính sự âm thầm đó lại khiến câu trả lời có thể bị bất kỳ script nào chạy trên trang đọc được, chứ không riêng đoạn mã đăng nhập cần đến nó.
Một cặp đúng/sai thực sự tương quan với điều gì
Một giá trị boolean nghe như chẳng nói lên được bao nhiêu — chỉ một bit. Nhưng bit đó không phải nhiễu ngẫu nhiên; nó là hệ quả của những đặc điểm phần cứng và phần mềm có thật, và nó phân loại người truy cập rõ nét hơn con số một bit gợi ý:
- Dòng thiết bị. Trình xác thực nền tảng thường dựa trên kho lưu khóa được phần cứng bảo vệ — secure enclave, chip TPM hoặc kho khóa phần cứng của Android. Máy tính để bàn đời cũ, nhiều máy ảo và một số cấu hình Linux không cung cấp được; còn trên điện thoại, câu trả lời còn phụ thuộc vào việc khóa màn hình đã thực sự được thiết lập hay chưa.
- Ngưỡng phiên bản hệ điều hành. Mỗi hệ điều hành bắt đầu hỗ trợ trình xác thực nền tảng từ một phiên bản cụ thể; câu trả lời
truengụ ý thiết bị đang ở phiên bản đó trở lên — chính xác hơn phần lớn các tín hiệu thụ động khác. - Engine trình duyệt. Không phải engine nào cũng triển khai API này theo cùng một cách và cùng một thời điểm, nên câu trả lời cũng khoanh vùng được engine hiển thị đang dùng — và cả phiên bản gần đúng của nó.
- Trạng thái được quản lý hoặc ảo hóa, trong một số cấu hình. Thiết bị không có bộ lưu trữ an toàn cấp phần cứng — một số máy ảo, một số bản cài đặt doanh nghiệp bị khóa chặt — có thể trả về
falsedù hệ điều hành và trình duyệt được khai báo lẽ ra phải hỗ trợ kiểm tra này.
Gộp lại, một cặp boolean không còn giống hai bit đơn thuần mà giống một bộ phân loại thô theo thế hệ thiết bị, ngưỡng hệ điều hành và engine — cơ chế đằng sau các tín hiệu này có trong hướng dẫn về dấu vân tay trình duyệt của chúng tôi — nhưng nó vẫn chỉ là bộ phân loại, không phải bảng tra cứu. Hãy coi mối tương quan này là có thật và hữu ích, chứ không phải bằng chứng về bất kỳ thiết bị cụ thể nào.
Thành thật về entropy: vài bit, không phải mã định danh
Rất dễ thổi phồng chuyện này — đừng làm vậy. Tự thân isUserVerifyingPlatformAuthenticatorAvailable() chỉ trả về một bit, isConditionalMediationAvailable() thêm tối đa một bit nữa. getClientCapabilities() trả về nhiều trường hơn, nhưng chúng không độc lập với nhau: phần lớn thay đổi đồng loạt theo hãng và phiên bản trình duyệt, vì mỗi nhà phát triển tung các tính năng này ra theo từng đợt. Tệ hơn cho ai muốn dùng nó làm mã định danh độc lập: gần như tất cả đều tương quan với thông tin hệ điều hành và phiên bản trình duyệt mà chuỗi User-Agent vốn đã tiết lộ. Thêm vào một dấu vân tay có sẵn, tín hiệu này hầu như không thu hẹp thêm được gì.
Giá trị thật của nó nằm ở chỗ khác: làm phép kiểm tra mâu thuẫn, không phải mã định danh. Hệ thống phát hiện không hỏi riêng lẻ "hàm này trả về giá trị gì?", mà hỏi giá trị đó có khớp với mọi thứ khác trang đã biết hay không. Một User-Agent tự nhận là điện thoại flagship hiện hành, đi kèm isUserVerifyingPlatformAuthenticatorAvailable() trả về false, là điểm vênh đáng gắn cờ: flagship thật đều có sẵn trình xác thực nền tảng, và hầu như máy nào cũng đã đặt khóa màn hình. Tương tự, một UA tự nhận là Chrome hoặc Safari đời mới mà hoàn toàn không có phương thức getClientCapabilities() thì không hợp lý. Đúng kiểu mâu thuẫn này xuất hiện trong hồ sơ trình duyệt chống phát hiện và tự động hóa giả mạo, nơi một thuộc tính được khai báo (chuỗi UA đời mới) không khớp với thuộc tính khác (không có trình xác thực nền tảng, không TPM, không secure enclave) vì hồ sơ được ráp từ nhiều mảnh chứ không đo từ một thiết bị thật.
Đây cũng là ý tưởng đằng sau dấu vân tay thiết bị đa phương tiện và các phép thăm dò phần cứng không cần quyền khác: không kiểm tra riêng lẻ nào nhận diện được bạn, nhưng mỗi kiểm tra là thêm một chỗ mà hồ sơ giả mạo phải "khai" cho khớp, và sự nhất quán thì khó làm giả hơn bất kỳ giá trị đơn lẻ nào.
Nghịch lý: sinh ra vì quyền riêng tư, lại thành một tín hiệu nhỏ
Đây là điều đáng để ngẫm. Passkey được thiết kế để thay thế mật khẩu — thứ bí mật dễ bị lừa đảo (phishing) đánh cắp và thường bị dùng lại trên nhiều trang — bằng các cặp khóa chỉ gắn với một trang duy nhất và không thể dùng để đối chiếu giữa các trang. Vậy mà chính API mang lại cải tiến đó, chỉ bằng việc tồn tại và trả lời true hoặc false, lại để lộ một tín hiệu thụ động nhỏ của riêng nó.
Hai lưu ý sau giữ cho nhận định này trung thực thay vì gây hoang mang. Thứ nhất, trình xác thực dạng phần mềm và trình xác thực ảo có tồn tại để phục vụ kiểm thử — DevTools của trình duyệt và môi trường CI có thể đăng ký một trình xác thực nền tảng ảo trả về true trên máy hoàn toàn không có phần cứng sinh trắc học, nên kiểm tra này không phải lúc nào cũng bảo đảm có phần cứng thật. Thứ hai, kiểm tra này không phân biệt được "không có trình xác thực" với "có nhưng chưa thiết lập". Một chiếc điện thoại có cảm biến vân tay nhưng chưa đặt khóa màn hình, hay một máy tính chưa từng đăng ký Windows Hello, có thể trông giống hệt — dưới góc nhìn của API này — một thiết bị vốn chưa bao giờ có phần cứng đó. Cả hai lưu ý đều dẫn về cùng một hướng: hãy coi đây là tín hiệu yếu, mang tính bổ trợ chứ không phải tín hiệu mạnh — giá trị của một phép kiểm tra mâu thuẫn nằm ở chỗ nó rẻ, chứ không phải ở chỗ tự nó đủ để kết luận.
Kiểm tra này không tiết lộ điều gì
Phần này cần nói thật chính xác, vì đây là chỗ việc nói quá gây tổn hại nhiều nhất đến lòng tin của người đọc:
- Nó không cho biết bạn có passkey cho trang này hay bất kỳ trang nào. Khả năng ("thiết bị này có dùng được passkey không?") và việc đăng ký ("đã có thông tin xác thực nào cho bên phụ thuộc này chưa?") là hai câu hỏi hoàn toàn tách biệt, và API này chỉ trả lời câu đầu.
- Nó không tiết lộ danh tính của bạn. Theo thiết kế, thông tin xác thực WebAuthn chỉ gắn với một bên phụ thuộc — passkey tạo cho một trang không thể bị trang khác đọc, liệt kê hay đối chiếu, và mỗi trang nhận một cặp khóa riêng thay vì một mã định danh dùng chung như cookie của bên thứ ba.
- Muốn làm gì với một thông tin xác thực thật vẫn cần bước xác minh người dùng rõ ràng. Kiểm tra khả năng diễn ra âm thầm, nhưng muốn đăng nhập bằng passkey thì người dùng vẫn phải hoàn tất xác minh sinh trắc học hoặc nhập mã PIN. Bản thân kiểm tra khả năng không đưa ai tiến gần hơn đến bước đó.
Việc giới hạn phạm vi như vậy là lựa chọn thiết kế có chủ đích, và nó tạo ra ranh giới quyền riêng tư vững hơn phần lớn các tín hiệu dấu vân tay thụ động: hãy so với xác thực thiết bị (device attestation), vốn đưa ra một khẳng định mạnh hơn nhiều, được ký bằng mật mã, về một thiết bị cụ thể, thay vì một bit khả năng mơ hồ, entropy thấp. Kiểm tra khả năng passkey và thông tin xác thực ẩn danh nằm gần nhau hơn trên phổ này — cả hai đều được xây dựng để chứng minh một thuộc tính hẹp mà không để lộ danh tính — còn xác thực thiết bị về bản chất là một khẳng định mạnh hơn và dễ định danh hơn. Nếu điều bạn quan tâm là cách các trang suy ra bạn đang đăng nhập vào những tài khoản nào, chứ không phải phần cứng của bạn làm được gì, thì đó là một cơ chế hoàn toàn khác — xem phát hiện đăng nhập liên trang.
Rút ra điều gì từ đây
Kiểm tra khả năng passkey là ví dụ rõ ràng cho ý tưởng kiểm tra tính nhất quán xuyên suốt blog này: các tín hiệu riêng lẻ yếu, khi được đối chiếu chéo với nhau, bắt được nhiều hơn bất kỳ tín hiệu mạnh đơn lẻ nào. Tự nó không phải là dấu vân tay, và cũng không phải rủi ro quyền riêng tư theo kiểu cookie theo dõi hay token xác thực thiết bị — nhưng nó là thêm một chỗ mà "bạn tự nhận mình là gì" và "thiết bị của bạn thực sự làm được gì" hoặc khớp nhau, hoặc không.
Hiện các công cụ của chúng tôi chưa kiểm tra hỗ trợ passkey, nhưng chúng cho thấy những tín hiệu mà câu trả lời về passkey sẽ được đối chiếu cùng: chạy công cụ kiểm tra dấu vân tay để xem User-Agent, nền tảng và các thuộc tính phần cứng mà trình duyệt của bạn để lộ, hoặc thử công cụ phát hiện bot để xem mâu thuẫn giữa các thuộc tính đó bị gắn cờ ra sao.
Câu hỏi thường gặp
Kiểm tra hỗ trợ passkey có cần tôi cấp quyền không?
Không. isUserVerifyingPlatformAuthenticatorAvailable(), isConditionalMediationAvailable() và getClientCapabilities() đều là các kiểm tra âm thầm dựa trên Promise — không hộp thoại xin quyền, không cần thao tác của người dùng, và trang không hiển thị gì cả.
Trang web có biết tôi đã lưu passkey hay chưa không?
Không. Các kiểm tra này chỉ báo cáo khả năng của thiết bị — có trình xác thực nền tảng hay không — chứ không cho biết đã có passkey nào được đăng ký cho trang đó hay bất kỳ trang nào. Việc đăng ký thông tin xác thực được giới hạn theo từng bên phụ thuộc và API này không để lộ thông tin đó.
Kết quả false từ kiểm tra này có phải lúc nào cũng có ý nghĩa?
Tự nó thì không. Kết quả này có thể có nghĩa là thiết bị thực sự không có trình xác thực nền tảng, hoặc có nhưng chưa được thiết lập (ví dụ chưa đặt khóa màn hình hay chưa đăng ký Windows Hello), hoặc — trong bối cảnh kiểm thử — chưa có trình xác thực ảo nào được đăng ký. Hãy coi đây là một tín hiệu yếu, mang tính bổ trợ, chứ không phải câu trả lời dứt khoát.
Điều này khác gì với xác thực thiết bị?
Khác rất xa về mức độ. Kiểm tra khả năng này chỉ là một giá trị boolean mơ hồ, entropy thấp, tương quan với dòng thiết bị và phiên bản hệ điều hành/trình duyệt. Xác thực thiết bị là một khẳng định được ký bằng mật mã, có phần cứng bảo chứng, về một thiết bị cụ thể — mạnh hơn và dễ định danh hơn nhiều.
Đọc thêm:
- Dấu vân tay trình duyệt là gì: Cách bảo vệ quyền riêng tư của bạn
- Xác thực thiết bị: Play Integrity và App Attest
- Thông tin xác thực ẩn danh: Chứng minh bạn là con người mà không bị theo dõi
- Dấu vân tay thiết bị đa phương tiện: enumerateDevices tiết lộ gì
- Phát hiện đăng nhập: Trang web biết bạn dùng dịch vụ nào bằng cách nào
- Dấu vân tay bất khả thi: Tổ hợp GPU/hệ điều hành/font tố cáo bạn


