בדיקת סיסמה
חלק מהיחידה אימות ואבטחה במסלול ה-Node.js של Coddy. שיעור 2 מתוך 17.
אי אפשר להפוך גיבוב, ולכן בתהליך התחברות לעולם לא קוראים מחדש את הסיסמה המאוחסנת. מגבבים את הניסיון באותה דרך ומשווים בין שני המפתחות. אותה דרך פירושה אותו מלח: מפצלים את salt:hash המאוחסן ומשתמשים שוב במלח שלו.
משווים באמצעות timingSafeEqual, ולא באמצעות ===. === נעצר בבייט הראשון ששונה, ולכן תוקף שמודד את משך הזמן של תשובות רבות יכול ללמוד איזה חלק מהניחוש שלו היה נכון. timingSafeEqual נמשך תמיד אותו פרק זמן. הוא משווה בין שני Buffer-ים באותו אורך וזורק שגיאה כאשר אורכיהם שונים.
import { timingSafeEqual } from 'node:crypto';
const expected = Buffer.from(process.env.API_KEY);
const given = Buffer.from(req.get('x-api-key') ?? '');
const ok = given.length === expected.length && timingSafeEqual(given, expected);Buffer.from(hex, 'hex') ממיר טקסט הקסדצימלי בחזרה לבתים.
אתגר
קל- בקובץ
passwords.js, הוסיפו וייצאו אתverifyPassword(password, stored): אמת כשהסיסמה היא זו שיצרה את הערך השמור. - בקובץ
server.js, הוסיפו אתPOST /loginעם{ username, password }: החזירו 200 עם{ username }, או 401 עם{ "error": "Wrong username or password" }במקרה של סיסמה שגויה או שם משתמש לא מוכר.
הירשמו בשם ada, התחברו, ואז התחברו עם סיסמה שגויה.
נסו בעצמכם
import { randomBytes, scrypt } from 'node:crypto';
import { promisify } from 'node:util';
const scryptAsync = promisify(scrypt);
// ממירה סיסמה לטקסט ש־signup שומר.
export async function hashPassword(password) {
const salt = randomBytes(16).toString('hex');
const key = await scryptAsync(password, salt, 64);
return `${salt}:${key.toString('hex')}`;
}