Kodokon kodokon.com

Sécurité d'une API en production

Blindez une API Node en production : CORS correct, rate limiting, en-têtes de sécurité et parades aux principaux risques OWASP API.

11 min · 3 questions

Ouvrir cette leçon dans Kodokon

L'OWASP maintient un top 10 dédié aux API. Les trois risques dominants : BOLA (Broken Object Level Authorization - accéder à l'objet d'un autre utilisateur en changeant un id), Broken Authentication (tokens mal validés, secrets faibles) et Unrestricted Resource Consumption (aucune limite de débit, de taille ou de temps). Remarquez qu'aucun des trois ne se corrige en installant une bibliothèque : ce sont des propriétés de votre conception. Commençons par les mécanismes transverses : CORS, rate limiting et en-têtes.

JAVASCRIPT
import { createServer } from "node:http";

const allowed = new Set(["https://app.example.com"]);

const server = createServer((req, res) => {
  const origin = req.headers.origin;
  if (origin && allowed.has(origin)) {
    res.setHeader(
      "Access-Control-Allow-Origin",
      origin,
    );
    res.setHeader("Vary", "Origin");
  }
  if (req.method === "OPTIONS") {
    res.setHeader(
      "Access-Control-Allow-Methods",
      "GET,POST,PUT,DELETE",
    );
    res.setHeader(
      "Access-Control-Allow-Headers",
      "content-type,authorization",
    );
    res.setHeader("Access-Control-Max-Age", "86400");
    res.writeHead(204).end();
    return;
  }
  res.setHeader("Content-Type", "application/json");
  res.end(JSON.stringify({ ok: true }));
});

server.listen(3000);
CORS à la main : liste blanche et preflight.

Comprenez bien ce que CORS n'est pas : une protection du serveur. curl ou un backend tiers ignorent totalement ces en-têtes ; seul le navigateur les applique, pour protéger ses utilisateurs. Trois règles d'expert : renvoyez l'origine exacte depuis une liste blanche (jamais d'écho aveugle de l'en-tête Origin), ajoutez Vary: Origin pour ne pas empoisonner les caches intermédiaires, et sachez que le joker * est refusé par les navigateurs dès que la requête transporte des cookies (credentials).

JAVASCRIPT
const WINDOW_MS = 60_000;
const LIMIT = 100;
const hits = new Map();

function check(ip) {
  const now = Date.now();
  const entry = hits.get(ip);
  if (!entry || now - entry.start >= WINDOW_MS) {
    hits.set(ip, { start: now, count: 1 });
    return { ok: true, remaining: LIMIT - 1 };
  }
  entry.count += 1;
  const remaining = Math.max(LIMIT - entry.count, 0);
  return { ok: entry.count <= LIMIT, remaining };
}

console.log(check("203.0.113.7"));
Fenêtre fixe en mémoire : renvoyez 429 si ok vaut false.

Les en-têtes de sécurité coûtent quelques lignes et bloquent des classes entières d'attaques : HSTS force le HTTPS pour les visites futures, X-Content-Type-Options: nosniff empêche le navigateur de deviner un type MIME, et une CSP restrictive (default-src 'none') neutralise l'exploitation d'une réponse API affichée par erreur dans un navigateur. Supprimez aussi tout en-tête qui révèle votre stack : Express ajoute X-Powered-By par défaut - désactivez-le avec app.disable('x-powered-by').

JAVASCRIPT
function setSecurityHeaders(res) {
  res.setHeader(
    "Strict-Transport-Security",
    "max-age=63072000; includeSubDomains",
  );
  res.setHeader("X-Content-Type-Options", "nosniff");
  res.setHeader(
    "Content-Security-Policy",
    "default-src 'none'; frame-ancestors 'none'",
  );
  res.setHeader("Cache-Control", "no-store");
}

export { setSecurityHeaders };
À appeler sur chaque réponse de l'API.

Quiz de validation

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

  1. Que protège réellement le mécanisme CORS ?
    • Le serveur contre les requêtes automatisées (curl, scripts)
    • L'utilisateur du navigateur contre les lectures cross-origin non autorisées
    • L'API contre les attaques par déni de service
  2. Pourquoi Access-Control-Allow-Origin: * est-il incompatible avec des requêtes envoyant des cookies ?
    • Les navigateurs rejettent cette combinaison pour éviter d'exposer des sessions authentifiées à n'importe quel site
    • Le serveur renvoie automatiquement une erreur 500
    • Le joker désactive les cookies côté serveur
  3. Une API authentifiée renvoie GET /invoices/42 à un utilisateur qui ne possède pas cette facture. De quel risque OWASP API s'agit-il ?
    • Injection
    • Broken Object Level Authorization (BOLA)
    • Security Misconfiguration
    • Server-Side Request Forgery