Hashe Passwörter mit einer teuren Funktion, wähle bewusst zwischen Sessions und JWT und schütze deine Routen mit einer Middleware.
Diese Lektion in Kodokon öffnenEin Passwort zu speichern heißt, sich auf einen künftigen Einbruch vorzubereiten: Gestalte das Hashing so, dass es den Diebstahl der Datenbank überlebt. Ein schneller Hash wie SHA-256 lässt sich auf einer GPU mit Milliarden Versuchen pro Sekunde durchprobieren. Du brauchst eine absichtlich teure Funktion - scrypt, das node:crypto mitbringt, oder bcrypt/argon2 - und ein zufälliges Salt pro Nutzer, das vorberechnete Tabellen nutzlos macht. Das Salt ist kein Geheimnis: Es wird im Klartext neben dem Hash gespeichert.
import {
randomBytes,
scryptSync,
timingSafeEqual,
} from 'node:crypto';
export function hashPassword(password) {
const salt = randomBytes(16).toString('hex');
const hash = scryptSync(password, salt, 64)
.toString('hex');
return `${salt}:${hash}`;
}
export function verifyPassword(password, stored) {
const [salt, hash] = stored.split(':');
const candidate = scryptSync(password, salt, 64);
const expected = Buffer.from(hash, 'hex');
return timingSafeEqual(candidate, expected);
}Zwei Modelle, um den Nutzer angemeldet zu halten. Die Session: eine undurchsichtige Kennung in einem httpOnly-Cookie, mit dem Zustand auf Server-Seite. Sofortiger Entzug, Cookie für JavaScript unsichtbar, aber ein gemeinsamer Speicher (Redis) wird nötig, sobald du eine zweite Instanz hinzufügst. Das JWT: der signierte Zustand steckt im Token, überprüfbar ohne jeden Speicher - ideal zwischen Diensten - aber vor Ablauf nicht widerrufbar. Der ehrliche Kompromiss: Sessions für eine klassische Webanwendung, kurzlebige JWTs für APIs, die von Dritten genutzt werden, oder für verteilte Architekturen.
import jwt from 'jsonwebtoken';
const SECRET = process.env.JWT_SECRET;
if (!SECRET) {
throw new Error('JWT_SECRET must be set');
}
export function signToken(user) {
return jwt.sign(
{ sub: user.id, email: user.email },
SECRET,
{ expiresIn: '15m' },
);
}Die Schutz-Middleware lagert die Zugriffskontrolle aus: Sie zieht das Token aus dem Header Authorization: Bearer, prüft es, hängt die dekodierte Identität an req.user oder macht mit einem 401 Schluss, das an die Fehler-Middleware der vorherigen Lektion geleitet wird. Wende sie auf Router-Ebene an (router.use(requireAuth)), um eine ganze Gruppe von Routen zu schützen, statt Route für Route.
import jwt from 'jsonwebtoken';
import { HttpError } from './http-error.js';
const SECRET = process.env.JWT_SECRET;
export function requireAuth(req, res, next) {
const header = req.headers.authorization ?? '';
const [scheme, token] = header.split(' ');
if (scheme !== 'Bearer' || !token) {
return next(new HttpError(401, 'Missing token'));
}
try {
req.user = jwt.verify(token, SECRET);
next();
} catch {
next(new HttpError(401, 'Invalid or expired token'));
}
}timingSafeEqual bei der Passwortprüfung da?