パスワードを確認する
CoddyのNode.jsジャーニー「認証とセキュリティ」セクションの一部。レッスン 2/17。
ハッシュは元に戻せないため、ログイン時に保存されたパスワードを読み戻すことはありません。試行されたパスワードを同じ方法でハッシュ化し、2つのキーを比較します。同じ方法とは、同じソルトを使うということです。保存された値を分割してsalt:hashを取り出し、そのソルトを再利用します。
===ではなく、timingSafeEqualで比較します。===は最初に異なるバイトで比較を止めるため、攻撃者は何度も試行して時間を計測することで、推測がどこまで正しかったかを知ることができます。timingSafeEqualは常に同じ時間がかかります。同じ長さの2つの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')は、16進数のテキストをバイトに戻します。
チャレンジ
簡単passwords.jsで、verifyPassword(password, stored)を追加してエクスポートします。パスワードが保存された値を生成したものなら、真になります。server.jsで、{ username, password }を受け取るPOST /loginを追加します。パスワードが間違っている場合、または名前が不明な場合は、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')}`;
}