Node.js : Sécuriser votre API avec CORS et Helmet

🏷️ Back-end 📅 12/04/2026 10:00:00 👤 Mezgani Said
Nodejs Security Cors Helmet Api
Node.js : Sécuriser votre API avec CORS et Helmet

Protéger votre application Node.js contre les attaques courantes avec CORS et le middleware Helmet. Configuration pratique et bonnes practices.

Introduction à la sécurité Node.js

La sécurité est un pilier fondamental pour toute application Node.js en production. Les attaques web évoluent constamment : injection SQL, XSS, CSRF, clickjacking, etc.

À retenir : CORS et Helmet sont deux middleware essentiels pour sécuriser votre API Node.js contre les attaques courantes.

Points clés :

  • CORS : contrôle d'accès entre origines (domaines différents)
  • Helmet : fixe les headers HTTP de sécurité
  • HTTP Headers : first line of defense
  • Validation : toujours valider les entrées utilisateur

CORS (Cross-Origin Resource Sharing)

CORS est un mécanisme qui permet à un navigateur d'accéder à des ressources d'une autre origine (domaine, port ou schéma différent).

Scénario sans CORS :

// Votre SPA Angular sur https://app.example.com
// Appelle votre API sur https://api.example.com
// ❌ BLOCKED par le navigateur (Same-Origin Policy)

Scénario avec CORS :

// Votre API répond avec les headers CORS appropriés
// Access-Control-Allow-Origin: https://app.example.com
// ✅ Navigateur accepte la requête
Important : CORS est une sécurité côté navigateur. Elle ne protège pas votre API d'attaques directes (tools, mobiles, etc.).

Configuration CORS pratique

Installation :

npm install cors

Base simple (autorise tous les domaines) :

const express = require('express');
const cors = require('cors');

const app = express();

// ⚠️ Attention : autorise TOUS les domaines
app.use(cors());

app.get('/api/data', (req, res) => {
    res.json({ message: 'Données publiques' });
});

app.listen(3000);

Configuration avec whitelist (recommandé) :

const corsOptions = {
    origin: ['https://app.example.com', 'https://admin.example.com'],
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    credentials: true,
    optionsSuccessStatus: 200
};

app.use(cors(corsOptions));

// Ou appliquer CORS sur des routes spécifiques
app.get('/api/public', cors(), (req, res) => {
    res.json({ data: 'Public' });
});

app.post('/api/protected', cors(corsOptions), (req, res) => {
    res.json({ data: 'Protected' });
});
À retenir : Toujours définir une whitelist d'origines en production. Jamais `origin: '*'` avec `credentials: true`.

Helmet : protection contre les attaques

Helmet configure les headers HTTP de sécurité pour protéger contre :

  • XSS (Cross-Site Scripting) - injection de scripts malveillants
  • Clickjacking - dissimuler votre app dans une iframe
  • MIME sniffing - exécuter du contenu comme un autre type
  • Insuffisance de HTTPS - forcer HTTPS via HSTS

Installation :

npm install helmet

Usage simple :

const express = require('express');
const helmet = require('helmet');

const app = express();

// Active tous les middleware Helmet par défaut
app.use(helmet());

app.get('/api/data', (req, res) => {
    res.json({ secure: true });
});

app.listen(3000);
Headers ajoutés automatiquement :
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Content-Security-Policy: base-uri 'self'; font-src 'self' https:; ...
Referrer-Policy: no-referrer

HTTP Headers essentiels

Voici les headers de sécurité les plus importants que Helmet configure :

1. Strict-Transport-Security (HSTS)

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
// Force HTTPS pendant 1 an, inclut sous-domaines et preload list

2. X-Content-Type-Options

X-Content-Type-Options: nosniff
// Empêche le navigateur de "deviner" le type MIME

3. X-Frame-Options (Clickjacking)

X-Frame-Options: DENY
// Empêche l'app d'être chargée dans une iframe

4. Content-Security-Policy (CSP)

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; img-src *
// Définit les sources autorisées pour chaque type de ressource

5. Referrer-Policy

Referrer-Policy: strict-origin-when-cross-origin
// Contrôle l'envoi du referrer

Combiner CORS et Helmet

La meilleure pratique est d'utiliser CORS et Helmet ensemble :

const express = require('express');
const cors = require('cors');
const helmet = require('helmet');

const app = express();

// 1. Sécurité des headers HTTP
app.use(helmet());

// 2. Contrôle d'accès multi-domaines
const corsOptions = {
    origin: process.env.NODE_ENV === 'production'
        ? ['https://app.example.com', 'https://admin.example.com']
        : 'http://localhost:4200',
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    credentials: true,
    allowedHeaders: ['Content-Type', 'Authorization'],
    maxAge: 86400
};

app.use(cors(corsOptions));

// 3. Parsing JSON sécurisé
app.use(express.json({ limit: '10kb' })); // Limiter la taille

// 4. Routes
app.get('/api/data', (req, res) => {
    res.json({ secure: true, cors: 'enabled' });
});

app.listen(3000, () => {
    console.info(('Serveur sécurisé lancé sur port 3000');
});
Ordre d'application : Helmet d'abord (headers), puis CORS (contrôle d'accès).

Configuration production vs développement

Développement (permissif) :

// .env.development
CORS_ORIGIN=http://localhost:4200
NODE_ENV=development

// app.js
const corsOptions = {
    origin: process.env.CORS_ORIGIN,
    credentials: true
};

if (process.env.NODE_ENV === 'development') {
    app.use(cors(corsOptions));
}

Production (restrictif) :

// .env.production
CORS_ORIGIN=https://app.example.com,https://admin.example.com
NODE_ENV=production

// app.js
const helmet = require('helmet');
const cors = require('cors');

app.use(helmet({
    hsts: {
        maxAge: 31536000,
        includeSubDomains: true,
        preload: true
    },
    contentSecurityPolicy: {
        directives: {
            defaultSrc: ["'self'"],
            scriptSrc: ["'self'", "'unsafe-inline'"],
            imgSrc: ['*']
        }
    }
}));

const allowedOrigins = process.env.CORS_ORIGIN.split(',');
app.use(cors({
    origin: allowedOrigins,
    credentials: true,
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    allowedHeaders: ['Content-Type', 'Authorization']
}));
Production Checklist :
  • ✅ HTTPS obligatoire (NODE_ENV=production le force)
  • ✅ HSTS préload enabled
  • ✅ CSP stricte
  • ✅ CORS whitelist pour domaines connus
  • ✅ X-Frame-Options: DENY

Bonnes pratiques de sécurité

1. Validez TOUJOURS les entrées utilisateur

const { body, validationResult } = require('express-validator');

app.post('/api/user', [
    body('email').isEmail().normalizeEmail(),
    body('password').isLength({ min: 8 })
], (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
    }
    // Traiter...
});

2. Utilisez des variables d'environnement, jamais hardcodez les secrets

// ❌ Mauvais
const secret = 'super-secret-key-12345';

// ✅ Bon
const secret = process.env.JWT_SECRET;
require('dotenv').config();

3. Limitez la taille des requêtes

app.use(express.json({ limit: '10kb' }));
app.use(express.urlencoded({ limit: '10kb' }));

4. Rattrapage d'erreurs global

app.use((err, req, res, next) => {
    console.error(err.stack);
    // Ne pas exposer les détails d'erreur en production
    const message = process.env.NODE_ENV === 'production'
        ? 'Une erreur interne s\'est produite'
        : err.message;
    res.status(err.status || 500).json({ error: message });
});

5. Rate limiting pour prévenir les attaques par brute force

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 min
    max: 100 // limit 100 requests par window
});

app.use('/api/', limiter);
À retenir : La sécurité n'est pas une destination, c'est un processus continu. Restez informé des nouvelles vulnérabilités et mettez à jour vos dépendances régulièrement.