Kodokon kodokon.com

Authentification : hachage, sessions vs JWT

Hachez les mots de passe avec une fonction coûteuse, choisissez entre sessions et JWT en connaissance de cause et protégez vos routes par middleware.

10 min · 3 questions

Ouvrir cette leçon dans Kodokon

Stocker un mot de passe, c'est préparer une future fuite : concevez le hachage pour résister au vol de la base. Un hachage rapide type SHA-256 se brute-force à plusieurs milliards d'essais par seconde sur GPU. Il faut une fonction volontairement coûteuse - scrypt, fourni par node:crypto, ou bcrypt/argon2 - et un sel aléatoire par utilisateur, qui rend les tables précalculées inutilisables. Le sel n'est pas un secret : il se stocke en clair à côté du hachage.

JAVASCRIPT
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);
}
Sel aléatoire, dérivation coûteuse, comparaison à temps constant.

Deux modèles pour maintenir l'utilisateur connecté. La session : un identifiant opaque dans un cookie httpOnly, l'état côté serveur. Révocation immédiate, cookie invisible pour le JavaScript, mais un stockage partagé (Redis) devient nécessaire dès la deuxième instance. Le JWT : l'état signé embarqué dans le jeton, vérifiable sans stockage - idéal entre services - mais irrévocable avant expiration. Le compromis honnête : sessions pour une application web classique, JWT courts pour les API consommées par des tiers ou les architectures distribuées.

JAVASCRIPT
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' },
  );
}
Secret obligatoire dès le démarrage, jetons à durée courte.

Le middleware de protection factorise le contrôle d'accès : il extrait le jeton de l'en-tête Authorization: Bearer, le vérifie, attache l'identité décodée à req.user, ou coupe court avec un 401 acheminé vers le middleware d'erreur de la leçon précédente. Appliquez-le au niveau d'un routeur (router.use(requireAuth)) pour protéger un groupe de routes entier plutôt que route par route.

JAVASCRIPT
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'));
  }
}
Vérifier, attacher req.user, ou 401 - rien d'autre.

Quiz de validation

Vérifiez que vous avez bien retenu les points clés de cette leçon.

  1. Pourquoi SHA-256 seul est-il inadapté au stockage de mots de passe ?
    • Parce que l'algorithme est cryptographiquement cassé
    • Parce qu'il est trop rapide : un GPU teste des milliards de candidats par seconde ; il faut une fonction lente et salée
    • Parce qu'il produit des collisions sur les mots de passe courts
    • Parce que Node ne l'implémente pas nativement
  2. Quel mécanisme permet de révoquer un accès immédiatement, avant toute expiration ?
    • Le JWT, car il embarque sa date d'expiration
    • La session côté serveur, car il suffit de supprimer son entrée du stockage
    • Les deux, de manière équivalente
  3. À quoi sert timingSafeEqual dans la vérification du mot de passe ?
    • À accélérer la comparaison des deux hachages
    • À comparer en temps constant pour ne pas révéler, via la durée de réponse, combien d'octets correspondent
    • À convertir les hachages hexadécimaux en Buffer