Builders Academy
Sécurité des APIs REST — Headers, Rate Limiting et Secrets
1 of 1

Chapitre 1 · Leçon 1

Sécuriser une API Express

Les 5 couches de sécurité incontournables

Section 1

Headers de sécurité avec Helmet

Step 1

Configurer Helmet

Helmet est un ensemble de middlewares Express qui configure des headers HTTP de sécurité. Il corrige de nombreuses vulnérabilités avec une seule ligne.

src/app.ts

typescript
1import express from "express";
2import helmet from "helmet";
3import cors from "cors";
4import rateLimit from "express-rate-limit";
5import { z } from "zod";
6
7const app = express();
8
9// ─── 1. Headers de sécurité ───────────────────────────
10app.use(helmet({
11  contentSecurityPolicy: {
12    directives: {
13      defaultSrc: ["'self'"],
14      scriptSrc: ["'self'"],
15      styleSrc: ["'self'", "'unsafe-inline'"],
16      imgSrc: ["'self'", "data:", "https:"],
17    },
18  },
19  crossOriginEmbedderPolicy: true,
20  crossOriginOpenerPolicy: { policy: "same-origin" },
21  hsts: {
22    maxAge: 31536000,
23    includeSubDomains: true,
24    preload: true,
25  },
26}));
27
28// Headers ajoutés automatiquement par Helmet :
29// X-Frame-Options: SAMEORIGIN          (anti-clickjacking)
30// X-Content-Type-Options: nosniff      (anti-MIME sniffing)
31// X-XSS-Protection: 0                  (désactivé car CSP est préféré)
32// Referrer-Policy: no-referrer          (privacy)
33// Strict-Transport-Security: ...        (force HTTPS)
34// X-DNS-Prefetch-Control: off
35
36// ─── 2. CORS configuré strictement ───────────────────
37app.use(cors({
38  origin: (origin, cb) => {
39    const allowed = ["https://usher.dev", "https://admin.usher.dev"];
40    if (!origin || allowed.includes(origin)) cb(null, true);
41    else cb(new Error(`CORS bloqué pour: ${origin}`));
42  },
43  credentials: true,
44  methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
45  allowedHeaders: ["Content-Type", "Authorization"],
46}));
47
48// ─── 3. Taille des requêtes limitée ──────────────────
49app.use(express.json({ limit: "100kb" }));  // Pas de mega-payloads
50app.use(express.urlencoded({ extended: false, limit: "100kb" }));

Step 2

Rate Limiting par route

Le rate limiting protège contre le brute force sur les endpoints d'authentification et les abus sur les endpoints coûteux.

src/app.ts

typescript
1import express from "express";
2import helmet from "helmet";
3import cors from "cors";
4import rateLimit from "express-rate-limit";
5import { z } from "zod";
6
7const app = express();
8
9// ─── 1. Headers de sécurité ───────────────────────────
10app.use(helmet({
11  contentSecurityPolicy: {
12    directives: {
13      defaultSrc: ["'self'"],
14      scriptSrc: ["'self'"],
15      styleSrc: ["'self'", "'unsafe-inline'"],
16      imgSrc: ["'self'", "data:", "https:"],
17    },
18  },
19  crossOriginEmbedderPolicy: true,
20  crossOriginOpenerPolicy: { policy: "same-origin" },
21  hsts: {
22    maxAge: 31536000,
23    includeSubDomains: true,
24    preload: true,
25  },
26}));
27
28// Headers ajoutés automatiquement par Helmet :
29// X-Frame-Options: SAMEORIGIN          (anti-clickjacking)
30// X-Content-Type-Options: nosniff      (anti-MIME sniffing)
31// X-XSS-Protection: 0                  (désactivé car CSP est préféré)
32// Referrer-Policy: no-referrer          (privacy)
33// Strict-Transport-Security: ...        (force HTTPS)
34// X-DNS-Prefetch-Control: off
35
36// ─── 2. CORS configuré strictement ───────────────────
37app.use(cors({
38  origin: (origin, cb) => {
39    const allowed = ["https://usher.dev", "https://admin.usher.dev"];
40    if (!origin || allowed.includes(origin)) cb(null, true);
41    else cb(new Error(`CORS bloqué pour: ${origin}`));
42  },
43  credentials: true,
44  methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
45  allowedHeaders: ["Content-Type", "Authorization"],
46}));
47
48// ─── 3. Taille des requêtes limitée ──────────────────
49app.use(express.json({ limit: "100kb" }));  // Pas de mega-payloads
50app.use(express.urlencoded({ extended: false, limit: "100kb" }));

Section 2

Gestion des secrets

Step 3

Variables d'environnement sécurisées

Ne jamais hardcoder des secrets. Valider les variables d'environnement au démarrage pour détecter les configurations manquantes immédiatement.

src/app.ts

typescript
1import express from "express";
2import helmet from "helmet";
3import cors from "cors";
4import rateLimit from "express-rate-limit";
5import { z } from "zod";
6
7const app = express();
8
9// ─── 1. Headers de sécurité ───────────────────────────
10app.use(helmet({
11  contentSecurityPolicy: {
12    directives: {
13      defaultSrc: ["'self'"],
14      scriptSrc: ["'self'"],
15      styleSrc: ["'self'", "'unsafe-inline'"],
16      imgSrc: ["'self'", "data:", "https:"],
17    },
18  },
19  crossOriginEmbedderPolicy: true,
20  crossOriginOpenerPolicy: { policy: "same-origin" },
21  hsts: {
22    maxAge: 31536000,
23    includeSubDomains: true,
24    preload: true,
25  },
26}));
27
28// Headers ajoutés automatiquement par Helmet :
29// X-Frame-Options: SAMEORIGIN          (anti-clickjacking)
30// X-Content-Type-Options: nosniff      (anti-MIME sniffing)
31// X-XSS-Protection: 0                  (désactivé car CSP est préféré)
32// Referrer-Policy: no-referrer          (privacy)
33// Strict-Transport-Security: ...        (force HTTPS)
34// X-DNS-Prefetch-Control: off
35
36// ─── 2. CORS configuré strictement ───────────────────
37app.use(cors({
38  origin: (origin, cb) => {
39    const allowed = ["https://usher.dev", "https://admin.usher.dev"];
40    if (!origin || allowed.includes(origin)) cb(null, true);
41    else cb(new Error(`CORS bloqué pour: ${origin}`));
42  },
43  credentials: true,
44  methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
45  allowedHeaders: ["Content-Type", "Authorization"],
46}));
47
48// ─── 3. Taille des requêtes limitée ──────────────────
49app.use(express.json({ limit: "100kb" }));  // Pas de mega-payloads
50app.use(express.urlencoded({ extended: false, limit: "100kb" }));

Topics and skills covered

  • Express
  • Helmet
  • Rate Limiting
  • Validation
  • Zod