Kunci sandi membuat akun pengguna lebih aman, lebih sederhana, dan lebih mudah digunakan.
Penggunaan kunci sandi meningkatkan keamanan, menyederhanakan login, dan menggantikan sandi. Tidak seperti sandi biasa, yang harus diingat dan dimasukkan secara manual oleh pengguna, kunci sandi menggunakan mekanisme kunci layar perangkat seperti biometrik atau PIN dan mengurangi risiko phishing serta pencurian kredensial.
Kunci sandi disinkronkan di seluruh perangkat menggunakan penyedia kunci sandi seperti Pengelola Sandi Google dan Rantai Kunci iCloud.
Kunci sandi harus dibuat, yang menyimpan kunci pribadi dengan aman ke penyedia kunci sandi beserta metadata yang diperlukan dan kunci publiknya yang disimpan di server Anda untuk autentikasi. Kunci pribadi mengeluarkan tanda tangan setelah verifikasi pengguna di domain yang valid sehingga kunci sandi tahan terhadap phishing. Kunci publik memverifikasi tanda tangan tanpa menyimpan kredensial sensitif, sehingga kunci sandi tahan terhadap pencurian kredensial.
Cara kerja pembuatan kunci sandi
Sebelum pengguna dapat login dengan kunci sandi, Anda harus membuat kunci sandi, mengaitkannya dengan akun pengguna, dan menyimpan kunci publiknya di server Anda.
Anda dapat meminta pengguna untuk membuat kunci sandi dalam salah satu situasi berikut:
- Selama atau setelah pendaftaran.
- Setelah login.
- Setelah login menggunakan kunci sandi dari perangkat lain (yaitu,
[authenticatorAttachment](https://web.dev/articles/passkey-form-autofill#authenticator-attachment)
adalahcross-platform
). - Di halaman khusus tempat pengguna dapat mengelola kunci sandi mereka.
Untuk membuat kunci sandi, Anda menggunakan WebAuthn API.
Empat komponen alur pendaftaran kunci sandi adalah:
- Backend: Menyimpan detail akun pengguna, termasuk kunci publik.
- Frontend: Berkomunikasi dengan browser dan mengambil data yang diperlukan dari backend.
- Browser: Menjalankan JavaScript Anda dan berinteraksi dengan WebAuthn API.
- Penyedia kunci sandi: Membuat dan menyimpan kunci sandi. Ini biasanya berupa pengelola sandi seperti Pengelola Sandi Google, atau kunci keamanan.

Sebelum membuat kunci sandi, pastikan sistem memenuhi prasyarat berikut:
Akun pengguna diverifikasi melalui metode yang aman (misalnya, email, verifikasi telepon, atau federasi identitas) dalam periode yang singkat.
Frontend dan backend dapat berkomunikasi dengan aman untuk bertukar data kredensial.
Browser mendukung WebAuthn dan pembuatan kunci sandi.
Kami dapat menunjukkan cara memeriksa sebagian besar di bagian berikut.
Setelah sistem memenuhi kondisi ini, proses berikut akan terjadi untuk membuat kunci sandi:
- Sistem memicu proses pembuatan kunci sandi saat pengguna memulai tindakan (misalnya, mengklik tombol "Buat Kunci Sandi" di halaman pengelolaan kunci sandi atau setelah menyelesaikan pendaftaran).
- Frontend meminta data kredensial yang diperlukan dari backend, termasuk informasi pengguna, verifikasi login, dan ID kredensial untuk mencegah duplikat.
- Frontend memanggil
navigator.credentials.create()
untuk meminta penyedia kunci sandi perangkat membuat kunci sandi menggunakan informasi dari backend. Perhatikan bahwa panggilan ini menampilkan promise. - Perangkat pengguna mengautentikasi pengguna menggunakan metode biometrik, PIN, atau pola untuk membuat kunci sandi.
- Penyedia kunci sandi membuat kunci sandi dan menampilkan kredensial kunci publik ke frontend, yang me-resolve promise.
- Frontend mengirim kredensial kunci publik yang dihasilkan ke backend.
- Backend menyimpan kunci publik dan data penting lainnya untuk autentikasi mendatang,
- Backend akan memberi tahu pengguna (misalnya, menggunakan email) untuk mengonfirmasi pembuatan kunci sandi dan mendeteksi potensi akses tidak sah.
Proses ini memastikan proses pendaftaran kunci sandi yang aman dan lancar bagi pengguna.
Kompatibilitas
Sebagian besar browser mendukung WebAuthn, dengan beberapa kekurangan kecil. Lihat passkeys.dev untuk mengetahui detail kompatibilitas browser dan OS.
Membuat kunci sandi baru
Untuk membuat kunci sandi baru, berikut adalah proses yang harus diikuti frontend:
- Periksa kompatibilitas.
- Mengambil informasi dari backend.
- Panggil WebAuth API untuk membuat kunci sandi.
- Kirim kunci publik yang ditampilkan ke backend.
- Simpan kredensial.
Bagian berikut menunjukkan cara melakukannya.
Memeriksa kompatibilitas
Sebelum menampilkan tombol "Buat kunci sandi baru", frontend harus memeriksa apakah:
- Browser mendukung WebAuthn dengan
PublicKeyCredential
.
- Perangkat mendukung pengautentikasi platform (dapat membuat kunci sandi dan
melakukan autentikasi dengan kunci sandi) dengan
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.
- Browser mendukung UI kondisional
WebAuthn dengan
PublicKeyCredenital.isConditionalMediationAvailable()
.
Cuplikan kode berikut menunjukkan cara memeriksa kompatibilitas sebelum menampilkan opsi terkait kunci sandi.
// 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
}
});
}
Dalam contoh ini, tombol Buat kunci sandi baru hanya boleh ditampilkan jika semua kondisi terpenuhi.
Mengambil informasi dari backend
Saat pengguna mengklik tombol, ambil informasi yang diperlukan dari
backend untuk memanggil navigator.credentials.create()
.
Cuplikan kode berikut menunjukkan objek JSON dengan informasi yang diperlukan untuk
memanggil 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,
}
}
Key-value pair dalam objek menyimpan informasi berikut:
challenge
: Verifikasi login yang dibuat server di ArrayBuffer untuk pendaftaran ini.rp.id
: ID RP (ID Pihak Tepercaya), domain, dan situs dapat menentukan domain atau akhiran yang dapat didaftarkan. Misalnya, jika asal RP adalahhttps://login.example.com:1337
, ID RP dapat berupalogin.example.com
atauexample.com
. Jika ID RP ditetapkan sebagaiexample.com
, pengguna dapat melakukan autentikasi dilogin.example.com
atau di subdomain apa pun diexample.com
. Lihat, Mengizinkan penggunaan kembali kunci sandi di seluruh situs Anda dengan Permintaan Asal Terkait untuk mengetahui informasi selengkapnya tentang hal ini.rp.name
: Nama RP (Pihak yang Mengandalkan). Ini tidak digunakan lagi di WebAuthn L3, tetapi disertakan karena alasan kompatibilitas.user.id
: ID pengguna unik di ArrayBuffer, yang dibuat saat pembuatan akun. Nama ini harus bersifat permanen, tidak seperti nama pengguna yang dapat diedit. ID pengguna mengidentifikasi akun, tetapi tidak boleh berisi informasi identitas pribadi (PII). Anda mungkin sudah memiliki ID pengguna di sistem, tetapi jika diperlukan, buat ID khusus untuk kunci sandi agar tidak berisi PII apa pun.user.name
: ID unik untuk akun yang akan dikenali pengguna, seperti alamat email atau nama pengguna mereka. ID unik ini akan ditampilkan di pemilih akun.user.displayName
: Nama yang diperlukan dan lebih mudah digunakan untuk akun. Kolom ini tidak harus unik, dan dapat berupa nama pilihan pengguna. Jika situs Anda tidak memiliki nilai yang sesuai untuk disertakan di sini, teruskan string kosong. Informasi ini mungkin ditampilkan di pemilih akun, bergantung pada browser.pubKeyCredParams
: Menentukan algoritma kunci publik yang didukung RP (pihak yang mengandalkan). Sebaiknya tetapkan ke[{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]
. Tindakan ini menentukan dukungan untuk ECDSA dengan P-256 dan RSA PKCS#1, serta memberikan cakupan lengkap (jika mendukung hal ini).excludeCredentials
: Daftar ID kredensial yang sudah terdaftar. Mencegah pendaftaran perangkat yang sama dua kali dengan memberikan daftar ID kredensial yang sudah terdaftar. Anggotatransports
, jika disediakan, harus berisi hasil pemanggilangetTransports()
selama pendaftaran setiap kredensial.authenticatorSelection.authenticatorAttachment
: Tetapkan ini ke"platform"
bersama denganhint: ['client-device']
jika pembuatan kunci sandi ini adalah upgrade dari sandi, misalnya dalam promosi setelah login."platform"
menunjukkan bahwa RP menginginkan pengautentikasi platform (pengautentikasi yang disematkan ke perangkat platform) yang tidak meminta, misalnya, untuk memasukkan kunci keamanan USB. Pengguna memiliki opsi yang lebih sederhana untuk membuat kunci sandi.authenticatorSelection.requireResidentKey
: Tetapkan ketrue
boolean. Kredensial yang dapat ditemukan (kunci tetap) menyimpan informasi pengguna ke kunci sandi dan memungkinkan pengguna memilih akun setelah autentikasi.authenticatorSelection.userVerification
: Menunjukkan apakah verifikasi pengguna yang menggunakan kunci layar perangkat adalah"required"
,"preferred"
, atau"discouraged"
. Setelan defaultnya adalah"preferred"
, yang berarti pengautentikasi dapat melewati verifikasi pengguna. Tetapkan ini ke"preferred"
atau hapus properti.
Sebaiknya buat objek di server, encode ArrayBuffer
dengan Base64URL, dan ambil dari frontend. Dengan cara ini, Anda dapat mendekode
payload menggunakan PublicKeyCredential.parseCreationOptionsFromJSON()
dan meneruskannya
langsung ke navigator.credentials.create()
.
Cuplikan kode berikut menunjukkan cara mengambil dan mendekode informasi yang diperlukan untuk membuat kunci sandi.
// 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);
...
Memanggil WebAuthn API untuk membuat kunci sandi
Panggil navigator.credentials.create()
untuk membuat kunci sandi baru. API menampilkan promise, menunggu interaksi pengguna yang menampilkan dialog modal.
// Invoke WebAuthn to create a passkey.
const credential = await navigator.credentials.create({
publicKey: options
});
Mengirim kredensial kunci publik yang ditampilkan ke backend
Setelah pengguna diverifikasi menggunakan kunci layar perangkat, kunci sandi akan dibuat dan promise diselesaikan dengan menampilkan objek PublicKeyCredential ke frontend.
Promise dapat ditolak karena berbagai alasan. Anda dapat menangani error ini
dengan memeriksa properti name
objek Error
:
InvalidStateError
: Kunci sandi sudah ada di perangkat. Tidak ada dialog error yang akan ditampilkan kepada pengguna. Situs tidak boleh memperlakukannya sebagai error. Pengguna ingin perangkat lokal didaftarkan dan perangkat tersebut telah didaftarkan.NotAllowedError
: Pengguna telah membatalkan operasi.AbortError
: Operasi telah dibatalkan.- Pengecualian lainnya: Terjadi error yang tidak terduga. Browser menampilkan dialog error kepada pengguna.
Objek kredensial kunci publik berisi properti berikut:
id
: ID yang dienkode Base64URL dari kunci sandi yang dibuat. ID ini membantu browser menentukan apakah kunci sandi yang cocok ada di perangkat saat autentikasi. Nilai ini harus disimpan pada database di backend.rawId
: Versi ArrayBuffer dari ID kredensial.response.clientDataJSON
: Data klien yang dienkode ArrayBuffer.response.attestationObject
: Objek pengesahan yang dienkode ArrayBuffer. Objek ini berisi informasi penting seperti ID RP, flag, dan kunci publik.authenticatorAttachment
: Menampilkan"platform"
saat kredensial ini dibuat di perangkat yang mendukung kunci sandi.type
: Kolom ini selalu ditetapkan ke"public-key"
.
Enkode objek dengan metode .toJSON()
, serialisasi dengan
JSON.stringify()
, lalu kirim ke server.
...
// 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
});
...
Menyimpan kredensial
Setelah menerima kredensial kunci publik di backend, sebaiknya gunakan library atau solusi sisi server, bukan menulis kode Anda sendiri untuk memproses kredensial kunci publik.
Kemudian, Anda dapat menyimpan informasi yang diambil dari kredensial ke database untuk digunakan di masa mendatang.
Daftar berikut menyertakan properti yang direkomendasikan untuk disimpan:
- ID Kredensial: ID kredensial yang ditampilkan dengan kredensial kunci publik.
- Nama kredensial: Nama kredensial. Beri nama setelah penyedia kunci sandi yang membuatnya, yang dapat diidentifikasi berdasarkan AAGUID.
- ID Pengguna: ID pengguna yang digunakan untuk membuat kunci sandi.
- Kunci publik: Kunci publik yang ditampilkan dengan kredensial kunci publik. Hal ini diperlukan untuk memverifikasi pernyataan kunci sandi.
- Tanggal dan waktu pembuatan: Catat tanggal dan waktu pembuatan kunci sandi. Hal ini berguna untuk mengidentifikasi kunci sandi.
- Tanggal dan waktu terakhir digunakan: Mencatat tanggal dan waktu terakhir saat pengguna menggunakan kunci sandi untuk login. Hal ini berguna untuk menentukan kunci sandi yang telah digunakan (atau tidak digunakan) oleh pengguna.
- AAGUID: ID unik penyedia kunci sandi.
- Flag Kelayakan Pencadangan: true jika perangkat memenuhi syarat untuk sinkronisasi kunci sandi. Informasi ini membantu pengguna mengidentifikasi kunci sandi yang dapat disinkronkan dan kunci sandi yang terikat perangkat (tidak dapat disinkronkan) di halaman pengelolaan kunci sandi.
Ikuti petunjuk yang lebih mendetail di Pendaftaran kunci sandi sisi server
Memberi sinyal jika pendaftaran gagal
Jika pendaftaran kunci sandi gagal, hal ini dapat menyebabkan kebingungan bagi pengguna. Jika ada kunci sandi di penyedia kunci sandi dan tersedia untuk pengguna, tetapi kunci publik terkait tidak disimpan ke sisi server, upaya login menggunakan kunci sandi tidak akan pernah berhasil dan sulit dipecahkan masalahnya. Pastikan untuk memberi tahu pengguna jika memang demikian.
Untuk mencegah kondisi tersebut, Anda dapat
memberi sinyal kunci sandi yang tidak dikenal ke penyedia kunci sandi menggunakan Signal API.
Dengan memanggil PublicKeyCredential.signalUnknownCredential()
dengan ID RP dan
ID kredensial, RP dapat memberi tahu penyedia kunci sandi bahwa kredensial
yang ditentukan telah dihapus atau tidak ada. Penyedia kunci sandi
yang akan menentukan cara menangani sinyal ini, tetapi jika didukung, kunci sandi terkait
diharapkan akan dihapus.
// 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.
...
}
}
Untuk mempelajari Signal API lebih lanjut, baca Memastikan kunci sandi konsisten dengan kredensial di server Anda dengan Signal API.
Mengirim notifikasi kepada pengguna
Mengirim notifikasi (seperti email) saat kunci sandi didaftarkan akan membantu pengguna mendeteksi akses akun yang tidak sah. Jika penyerang membuat kunci sandi tanpa sepengetahuan pengguna, kunci sandi tersebut tetap tersedia untuk penyalahgunaan di masa mendatang, bahkan setelah sandi diubah. Notifikasi akan memberi tahu pengguna dan membantu mencegah hal ini.
Checklist
- Verifikasi pengguna (sebaiknya menggunakan email atau metode aman) sebelum mengizinkan mereka membuat kunci sandi.
- Mencegah pembuatan kunci sandi duplikat untuk penyedia kunci sandi yang sama menggunakan
excludeCredentials
. - Simpan AAGUID untuk mengidentifikasi penyedia kunci sandi dan memberi nama kredensial untuk pengguna.
- Memberi sinyal jika upaya pendaftaran kunci sandi gagal dengan
PublicKeyCredential.signalUnknownCredential()
. - Kirim notifikasi kepada pengguna setelah membuat dan mendaftarkan kunci sandi untuk akunnya.
Resource
- Pendaftaran kunci sandi sisi server
- Dokumen Apple: Mengautentikasi Pengguna Melalui Layanan Web
- Dokumen Google: Login tanpa sandi dengan kunci sandi
Langkah berikutnya: Login dengan kunci sandi melalui isi otomatis formulir.