Menu
CoddyTech

Memeriksa Kata Sandi

Termasuk bagian Autentikasi dan Keamanan dalam perjalanan Node.js di Coddy. Pelajaran 6 dari 24.

Proses login menghasilkan kunci dari kata sandi yang dicoba dengan menggunakan salt yang disimpan dan panjang kunci asli. Salt baru akan menghasilkan kunci yang berbeda, bahkan jika kata sandinya benar. Kata sandi asli tidak pernah didekode dari penyimpanan.

// Kode undangan disimpan sebagai salt.keyHex
const [salt, keyHex] = savedInvite.split('.');
const expected = Buffer.from(keyHex, 'hex');
const attempted = await scryptAsync(inviteCode, salt, 32);

Bandingkan urutan byte yang dihasilkan menggunakan timingSafeEqual, setelah memastikan panjangnya sama. Cara ini menghindari penghentian dini yang bergantung pada konten dan dapat mengungkap informasi melalui waktu eksekusi. Buffer === memeriksa identitas, sedangkan perbandingan string biasa tidak memberikan jaminan waktu konstan. Fungsi kata sandimu menggunakan pemisah titik dua pada record kata sandi dan kunci 64 byte. Rute POST /login yang kamu tambahkan menggunakan hasilnya untuk mengembalikan 200 atau 401.

challenge icon

Tantangan

Mudah

Langkah ini membuka proyek API catatan pribadi yang sudah disiapkan untuk kursus, bukan file proyekmu dari Never Store Passwords (termasuk passwords.js). Pekerjaanmu sebelumnya tetap berada di pelajarannya sendiri. Baca file yang disediakan sebelum mengedit passwords.js. Salinan ini berisi fungsi pembantu hashing kata sandi dan route debug yang disediakan.

Sekarang signup menyimpan hash dengan salt. Tambahkan login yang memeriksa kata sandi berdasarkan yang disimpan signup.

  1. Di passwords.js, tambahkan dan ekspor fungsi async verifyPassword(password, stored). stored adalah nilai yang disimpan signup, seperti f2bb...f065:c3f7...dbd2. Kembalikan true jika password adalah kata sandi yang menghasilkan nilai itu, dan false jika tidak:
    • Ambil salt dari bagian sebelum titik dua.
    • Hash password dengan salt itu menjadi kunci 64 byte, dengan cara yang sama seperti hashPassword.
    • Dekode kunci tersimpan setelah titik dua dari hex. Jika panjang byte kedua kunci berbeda, kembalikan false tanpa memanggil timingSafeEqual, seperti dalam Membandingkan Byte Kunci. Jika tidak, kembalikan hasil perbandingannya.
  2. Di server.js, impor verifyPassword di samping hashPassword. Tambahkan POST /login. Endpoint ini menerima body yang sama seperti signup, misalnya {"username": "ada", "password": "correct horse"}.
    • Cari pengguna dengan username tersebut. verifyPassword berjalan lambat dan mengembalikan promise, jadi tunggu hingga selesai.
    • Jika pengguna ditemukan dan kata sandinya benar, berikan respons dengan status 200 dan {"username": "ada"}.
    • Jika tidak, berikan respons dengan status 401 dan {"error": "Wrong username or password"}. 401 Tidak Terotorisasi berarti permintaan tersebut tidak membuktikan siapa yang mengajukannya.
    • Kirim respons 401 yang sama untuk kata sandi yang salah dan username yang belum pernah digunakan untuk signup. Dengan begitu, body respons tidak akan memberi tahu siapa pun username mana yang terdaftar.

Coba: Tekan tombol “Jalankan”. Kirim POST /signup dengan {"username": "ada", "password": "correct horse"}: status 201. Kirim POST /login dengan body yang sama: status 200 dan {"username": "ada"}. Kirim POST /login dengan {"username": "ada", "password": "wrong horse"}: status 401 dan {"error": "Wrong username or password"}. Kirim POST /login dengan {"username": "nobody", "password": "correct horse"}: status 401 yang sama.

Coba sendiri

import { randomBytes, scrypt } from 'node:crypto';
import { promisify } from 'node:util';

const scryptAsync = promisify(scrypt);

// Mengubah kata sandi menjadi teks yang disimpan oleh signup.
export async function hashPassword(password) {
    const salt = randomBytes(16).toString('hex');
    const key = await scryptAsync(password, salt, 64);
    return `${salt}:${key.toString('hex')}`;
}

Semua pelajaran di Autentikasi dan Keamanan

Latihan sendiri: Playground Express