Ringkasan
Berikut adalah ringkasan umum langkah-langkah utama yang terlibat dalam autentikasi kunci sandi:

- Tentukan verifikasi login dan opsi lain yang diperlukan untuk melakukan autentikasi dengan kunci sandi. Kirimkan ke klien, sehingga Anda dapat meneruskannya ke panggilan autentikasi kunci sandi (
navigator.credentials.getdi web). Setelah pengguna mengonfirmasi autentikasi kunci sandi, panggilan autentikasi kunci sandi akan diselesaikan dan menampilkan kredensial (PublicKeyCredential). Kredensial tersebut berisi pernyataan autentikasi.
- Verifikasi pernyataan autentikasi.
- Jika pernyataan autentikasi valid, autentikasi pengguna.
Bagian berikut membahas secara mendalam setiap langkah.
Membuat tantangan
Dalam praktiknya, tantangan adalah array byte acak, yang direpresentasikan sebagai objek ArrayBuffer.
// Example challenge, base64URL-encoded
weMLPOSx1VfSnMV6uPwDKbjGdKRMaUDGxeDEUTT5VN8
Untuk memastikan tantangan memenuhi tujuannya, Anda harus:
- Pastikan verifikasi login yang sama tidak pernah digunakan lebih dari sekali. Buat verifikasi baru pada setiap upaya login. Hapus tantangan setelah setiap upaya login, baik berhasil maupun gagal. Hapus juga tantangan setelah durasi tertentu. Jangan terima verifikasi login yang sama dalam satu respons lebih dari sekali.
- Pastikan verifikasi aman secara kriptografis. Tantangan harus hampir mustahil untuk ditebak. Untuk membuat verifikasi yang aman secara kriptografis di sisi server, sebaiknya gunakan library sisi server FIDO yang Anda percayai. Jika Anda membuat tantangan sendiri, gunakan fungsi kriptografis bawaan yang tersedia di stack teknologi Anda, atau cari library yang dirancang untuk kasus penggunaan kriptografis. Contohnya mencakup iso-crypto di Node.js, atau secrets di Python. Sesuai dengan spesifikasi, tantangan harus memiliki panjang minimal 16 byte agar dianggap aman.
Setelah Anda membuat verifikasi, simpan verifikasi tersebut dalam sesi pengguna untuk memverifikasinya nanti.
Membuat opsi permintaan kredensial
Buat opsi permintaan kredensial sebagai objek publicKeyCredentialRequestOptions.
Untuk melakukannya, andalkan library sisi server FIDO Anda. Biasanya, library ini akan menawarkan fungsi utilitas yang dapat membuat opsi ini untuk Anda. SimpleWebAuthn menawarkan, misalnya, generateAuthenticationOptions.
publicKeyCredentialRequestOptions harus berisi semua informasi yang diperlukan untuk autentikasi kunci sandi. Teruskan informasi ini ke fungsi di library sisi server FIDO yang bertanggung jawab untuk membuat objek publicKeyCredentialRequestOptions.
Beberapa kolom publicKeyCredentialRequestOptions dapat berupa konstanta. Lainnya harus ditentukan secara dinamis di server:
rpId: ID RP yang Anda harapkan untuk dikaitkan dengan kredensial, misalnyaexample.com. Autentikasi hanya akan berhasil jika ID RP yang Anda berikan di sini cocok dengan ID RP yang terkait dengan kredensial. Untuk mengisi ID RP, gunakan nilai yang sama dengan ID RP yang Anda tetapkan dipublicKeyCredentialCreationOptionsselama pendaftaran kredensial.challenge: Bagian data yang akan ditandatangani oleh penyedia kunci sandi untuk membuktikan bahwa pengguna memiliki kunci sandi pada saat permintaan autentikasi. Tinjau detail di bagian Membuat tantangan.allowCredentials: Array kredensial yang dapat diterima untuk autentikasi ini. Teruskan array kosong agar pengguna dapat memilih kunci sandi yang tersedia dari daftar yang ditampilkan oleh browser. Tinjau Mengambil verifikasi dari server RP dan Pembahasan mendalam tentang kredensial yang dapat ditemukan untuk mengetahui detailnya.userVerification: Menunjukkan apakah verifikasi pengguna yang menggunakan kunci layar perangkat "diperlukan", "lebih disukai", atau "tidak disarankan". Tinjau Mengambil verifikasi dari server RP.timeout: Durasi (dalam milidetik) yang dapat digunakan pengguna untuk menyelesaikan autentikasi. Durasi ini harus cukup lama, dan lebih singkat daripada masa aktifchallenge. Nilai default yang direkomendasikan adalah 5 menit, tetapi Anda dapat meningkatkannya hingga 10 menit, yang masih dalam rentang yang direkomendasikan. Waktu tunggu yang lama dapat diterima jika Anda memperkirakan pengguna akan menggunakan alur kerja hybrid, yang biasanya memerlukan waktu sedikit lebih lama. Jika waktu operasi habis,NotAllowedErrorakan ditampilkan.
Setelah Anda membuat publicKeyCredentialRequestOptions, kirimkan ke klien.
challenge decoding terjadi di sisi klien.Kode contoh: membuat opsi permintaan kredensial
Kami menggunakan library SimpleWebAuthn dalam contoh kami. Di sini, kita menyerahkan pembuatan opsi permintaan kredensial ke fungsi generateAuthenticationOptions.
import {
generateAuthenticationOptions,
} from '@simplewebauthn/server';
router.post('/signinRequest', csrfCheck, async (req, res) => {
// Ensure you nest calls in try/catch blocks.
// If something fails, throw an error with a descriptive error message.
// Return that message with an appropriate error code to the client.
try {
// Use the generateAuthenticationOptions function from SimpleWebAuthn
const options = await generateAuthenticationOptions({
rpID: process.env.HOSTNAME,
allowCredentials: [],
});
// Save the challenge in the user session
req.session.challenge = options.challenge;
return res.json(options);
} catch (e) {
console.error(e);
return res.status(400).json({ error: e.message });
}
});
Memverifikasi dan memproses login pengguna
Jika navigator.credentials.get berhasil di-resolve di klien, objek PublicKeyCredential akan ditampilkan.
navigator.credentials.get menampilkan PublicKeyCredential.response adalah AuthenticatorAssertionResponse. Ini mewakili respons penyedia kunci sandi terhadap petunjuk klien untuk membuat hal yang diperlukan untuk mencoba dan mengautentikasi dengan kunci sandi di RP. File tersebut berisi:
response.authenticatorDatadanresponse.clientDataJSON, seperti pada langkah pendaftaran kunci sandi.response.signatureyang berisi tanda tangan atas nilai-nilai ini.
Kirim objek PublicKeyCredential ke server.
Di server, lakukan hal berikut:
- Kumpulkan informasi yang Anda perlukan untuk memverifikasi pernyataan dan mengautentikasi pengguna:
- Dapatkan verifikasi yang diharapkan yang Anda simpan dalam sesi saat Anda membuat opsi autentikasi.
- Dapatkan origin dan ID RP yang diharapkan.
- Temukan siapa pengguna tersebut di database Anda. Dalam kasus kredensial yang dapat ditemukan, Anda tidak tahu siapa pengguna yang membuat permintaan autentikasi. Untuk mengetahuinya, Anda memiliki dua opsi:
- Opsi 1: Gunakan
response.userHandledi objekPublicKeyCredential. Di tabel Users, caripasskey_user_idyang cocok denganuserHandle. - Opsi 2: Gunakan kredensial
idyang ada di objekPublicKeyCredential. Di tabel Kredensial kunci publik, cari kredensialidyang cocok dengan kredensialidyang ada di objekPublicKeyCredential. Kemudian, cari pengguna yang sesuai menggunakan kunci asingpasskey_user_idke tabel Users Anda.
- Opsi 1: Gunakan
- Temukan informasi kredensial kunci publik di database Anda yang cocok dengan pernyataan autentikasi yang Anda terima. Untuk melakukannya, di tabel Public key credentials, cari kredensial
idyang cocok dengan kredensialidyang ada di objekPublicKeyCredential.
Verifikasi pernyataan autentikasi. Serahkan langkah verifikasi ini ke library sisi server FIDO Anda, yang biasanya akan menawarkan fungsi utilitas untuk tujuan ini. SimpleWebAuthn menawarkan, misalnya,
verifyAuthenticationResponse. Pelajari apa yang terjadi di balik layar di Lampiran: verifikasi respons autentikasi.Hapus verifikasi, baik verifikasi berhasil maupun tidak, untuk mencegah serangan replay.
Memproses login pengguna. Jika verifikasi berhasil, perbarui informasi sesi untuk menandai pengguna sebagai login. Anda mungkin juga ingin menampilkan objek
userke klien, sehingga frontend dapat menggunakan informasi yang terkait dengan pengguna yang baru login.
Contoh kode: memverifikasi dan memproses login pengguna
Kami menggunakan library SimpleWebAuthn dalam contoh kami. Di sini, kita menyerahkan verifikasi respons autentikasi ke fungsi verifyAuthenticationResponse.
import {
verifyAuthenticationResponse,
} from '@simplewebauthn/server';
import { isoBase64URL } from '@simplewebauthn/server/helpers';
router.post('/signinResponse', csrfCheck, async (req, res) => {
const response = req.body;
const expectedChallenge = req.session.challenge;
const expectedOrigin = getOrigin(req.get('User-Agent'));
const expectedRPID = process.env.HOSTNAME;
// Ensure you nest verification function calls in try/catch blocks.
// If something fails, throw an error with a descriptive error message.
// Return that message with an appropriate error code to the client.
try {
// Find the credential stored to the database by the credential ID
const cred = Credentials.findById(response.id);
if (!cred) {
throw new Error('Credential not found.');
}
// Find the user - Here alternatively we could look up the user directly
// in the Users table via userHandle
const user = Users.findByPasskeyUserId(cred.passkey_user_id);
if (!user) {
throw new Error('User not found.');
}
// Prepare credential information for verification
const credential = {
id: cred.id,
publicKey: isoBase64URL.toBuffer(cred.publicKey),
transports: cred.transports,
};
// Verify the credential
const { verified, authenticationInfo } = await verifyAuthenticationResponse({
response,
expectedChallenge,
expectedOrigin,
expectedRPID,
credential,
requireUserVerification: false,
});
if (!verified) {
throw new Error('User verification failed.');
}
// Kill the challenge for this session.
delete req.session.challenge;
req.session.username = user.username;
req.session['signed-in'] = 'yes';
return res.json(user);
} catch (e) {
delete req.session.challenge;
console.error(e);
return res.status(400).json({ error: e.message });
}
});
Lampiran: verifikasi respons autentikasi
Memverifikasi respons autentikasi terdiri dari pemeriksaan berikut:
- Pastikan ID RP cocok dengan situs Anda.
- Pastikan asal permintaan cocok dengan asal login situs Anda. Untuk aplikasi Android, tinjau Verifikasi origin.
- Pastikan perangkat dapat memberikan tantangan yang Anda berikan.
- Pastikan bahwa selama autentikasi, pengguna telah mengikuti persyaratan yang Anda tetapkan sebagai RP. Jika Anda memerlukan verifikasi pengguna, pastikan flag
uv(pengguna terverifikasi) diauthenticatorDataadalahtrue. Pastikan flagup(kehadiran pengguna) diauthenticatorDataadalahtrue, karena kehadiran pengguna selalu diperlukan untuk kunci sandi. - Verifikasi tanda tangan. Untuk memverifikasi tanda tangan, Anda memerlukan:
- Tanda tangan, yang merupakan verifikasi bertanda tangan:
response.signature - Kunci publik, untuk memverifikasi tanda tangan.
- Data asli yang ditandatangani. Ini adalah data yang tanda tangannya akan diverifikasi.
- Algoritma kriptografi yang digunakan untuk membuat tanda tangan.
- Tanda tangan, yang merupakan verifikasi bertanda tangan:
Untuk mempelajari langkah-langkah ini lebih lanjut, lihat kode sumber SimpleWebAuthn untuk verifyAuthenticationResponse atau pelajari daftar lengkap verifikasi dalam spesifikasi.