Intermediaire 3 min de lecture · 571 mots

Sécuriser une Application Web : Headers, CORS et CSP

Estimated reading time: 3 minutes

Introduction : La Première Ligne de Défense

Les headers HTTP de sécurité constituent la première ligne de défense de toute application web moderne. Souvent négligés, ces en-têtes peuvent prévenir des attaques courantes comme le Cross-Site Scripting (XSS), le clickjacking, et les fuites de données. En 2025, avec l’augmentation des attaques web sophistiquées, configurer correctement ces headers n’est plus optionnel.

Ce guide complet explore les headers de sécurité essentiels, CORS (Cross-Origin Resource Sharing) et CSP (Content Security Policy) avec des exemples pratiques et des configurations production-ready.

Headers de Sécurité Essentiels

Vue d’Ensemble des Headers Critiques

Header Protection Priorité
Content-Security-Policy XSS, injection Critique
Strict-Transport-Security Man-in-the-middle Critique
X-Frame-Options Clickjacking Élevée
X-Content-Type-Options MIME sniffing Élevée
Referrer-Policy Fuite d’informations Moyenne
Permissions-Policy Accès fonctionnalités Moyenne

Configuration Apache : Headers de Sécurité

# .htaccess - Configuration complète des headers de sécurité

headers.c>
    # 1. HTTP Strict Transport Security (HSTS)
    # Force HTTPS pour 2 ans, incluant sous-domaines
    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"

    # 2. X-Frame-Options
    # Empêche le site d'être chargé dans une iframe (protection clickjacking)
    Header always set X-Frame-Options "DENY"

    # 3. X-Content-Type-Options
    # Empêche le navigateur de deviner le type MIME
    Header always set X-Content-Type-Options "nosniff"

    # 4. X-XSS-Protection (déprécié mais utile pour anciens navigateurs)
    # Mode block : bloque la page si XSS détecté
    Header always set X-XSS-Protection "1; mode=block"

    # 5. Referrer-Policy
    # Ne transmet l'URL complète qu'aux mêmes origines
    Header always set Referrer-Policy "strict-origin-when-cross-origin"

    # 6. Permissions-Policy (anciennement Feature-Policy)
    # Désactive les fonctionnalités non utilisées
    Header always set Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()"

    # 7. X-Permitted-Cross-Domain-Policies
    # Empêche Adobe Flash/PDF d'accéder aux données
    Header always set X-Permitted-Cross-Domain-Policies "none"

    # 8. Cache-Control pour pages sensibles
    
        Header always set Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
        Header always set Pragma "no-cache"
        Header always set Expires "0"
    

Configuration Nginx

# nginx.conf ou dans votre bloc server

server {
    listen 443 ssl http2;
    servername example.com;

    # Configuration SSL/TLS
    sslcertificate /path/to/cert.pem;
    sslcertificatekey /path/to/key.pem;
    sslprotocols TLSv1.2 TLSv1.3;
    sslciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
    sslpreferserverciphers on;

    # Headers de sécurité
    addheader Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    addheader X-Frame-Options "DENY" always;
    addheader X-Content-Type-Options "nosniff" always;
    addheader X-XSS-Protection "1; mode=block" always;
    addheader Referrer-Policy "strict-origin-when-cross-origin" always;
    addheader Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

    # Content Security Policy (détaillé plus loin)
    addheader Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" always;

    # Désactiver les méthodes HTTP non utilisées
    if ($requestmethod !~ ^(GET|HEAD|POST|PUT|DELETE)$ ) {
        return 405;
    }

    location / {
        tryfiles $uri $uri/ /index.php?$querystring;
    }

    location ~ .php$ {
        fastcgipass unix:/var/run/php/php8.2-fpm.sock;
        fastcgiindex index.php;
        include fastcgiparams;
        fastcgiparam SCRIPTFILENAME $documentroot$fastcgiscriptname;

        # Headers de sécurité pour pages dynamiques
        fastcgihideheader X-Powered-By;
    }
}

Configuration PHP : Headers Programmatiques


  Gestionnaire de headers de sécurité
 /
class SecurityHeadersManager {
    private bool $isHttps;
    private string $environment;

    public function construct(string $environment = 'production') {
        $this->environment = $environment;
        $this->isHttps = (
            (!empty($SERVER['HTTPS']) && $SERVER['HTTPS'] !== 'off') ||
            $SERVER['SERVERPORT'] == 443 ||
            (!empty($SERVER['HTTPXFORWARDEDPROTO']) && $SERVER['HTTPXFORWARDEDPROTO'] === 'https')
        );
    }

    /
      Applique tous les headers de sécurité
     /
    public function applySecurityHeaders(): void {
        // Supprimer les headers révélant des informations
        headerremove('X-Powered-By');
        headerremove('Server');

        // HSTS (uniquement en HTTPS)
        if ($this->isHttps) {
            header(
                'Strict-Transport-Security: max-age=63072000; includeSubDomains; preload',
                true
            );
        }

        // Protection clickjacking
        header('X-Frame-Options: DENY', true);

        // Empêcher le MIME sniffing
        header('X-Content-Type-Options: nosniff', true);

        // XSS Protection (navigateurs anciens)
        header('X-XSS-Protection: 1; mode=block', true);

        // Referrer Policy
        header('Referrer-Policy: strict-origin-when-cross-origin', true);

        // Permissions Policy
        header('Permissions-Policy: geolocation=(), microphone=(), camera=()', true);

        // CSP (Content Security Policy)
        $this->applyCSP();

        // Cache control pour pages sensibles
        if ($this->isSensitivePage()) {
            $this->applySensitivePageHeaders();
        }
    }

    /
      Applique le Content Security Policy
     /
    private function applyCSP(): void {
        $nonce = $this->generateCSPNonce();

        // Stocker le nonce pour utilisation dans les templates
        define('CSPNONCE', $nonce);

        $cspDirectives = [
            "default-src 'self'",
            "script-src 'self' 'nonce-{$nonce}' https://cdn.jsdelivr.net",
            "style-src 'self' 'nonce-{$nonce}' https://fonts.googleapis.com",
            "img-src 'self' data: https:",
            "font-src 'self' https://fonts.gstatic.com",
            "connect-src 'self' https://api.example.com",
            "frame-ancestors 'none'",
            "base-uri 'self'",
            "form-action 'self'",
            "upgrade-insecure-requests"
        ];

        // En développement, ajouter des directives plus permissives
        if ($this->environment === 'development') {
            $cspDirectives[] = "script-src 'self' 'unsafe-eval' 'nonce-{$nonce}'";
        }

        header('Content-Security-Policy: ' . implode('; ', $cspDirectives), true);

        // CSP en mode report uniquement (pour tester avant déploiement)
        // header('Content-Security-Policy-Report-Only: ' . implode('; ', $cspDirectives), true);
    }

    /
      Génère un nonce aléatoire pour CSP
     /
    private function generateCSPNonce(): string {
        return base64encode(randombytes(16));
    }

    /
      Vérifie si la page est sensible (login, checkout, etc.)
     /
    private function isSensitivePage(): bool {
        $uri = $SERVER['REQUESTURI'] ?? '';
        $sensitivePaths = ['/login', '/checkout', '/account', '/admin'];

        foreach ($sensitivePaths as $path) {
            if (strpos($uri, $path) !== false) {
                return true;
            }
        }

        return false;
    }

    /
      Headers spécifiques pour pages sensibles
     /
    private function applySensitivePageHeaders(): void {
        header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0', true);
        header('Pragma: no-cache', true);
        header('Expires: 0', true);
    }
}

// Utilisation dans votre bootstrap/index.php
$securityHeaders = new SecurityHeadersManager($ENV['APPENV'] ?? 'production');
$securityHeaders->applySecurityHeaders();

Content Security Policy (CSP) : Protection XSS Avancée

CSP : Comprendre les Directives

Le CSP est le header le plus puissant contre les attaques XSS. Il définit quelles sources sont autorisées pour chaque type de contenu.


  Générateur de CSP configuré
 /
class CSPBuilder {
    private array $directives = [];
    private ?string $nonce = null;
    private bool $reportOnly = false;

    public function construct() {
        $this->nonce = base64encode(randombytes(16));
    }

    /
      Configure la directive default-src
     /
    public function setDefaultSrc(array $sources): self {
        $this->directives['default-src'] = $sources;
        return $this;
    }

    /
      Configure les sources JavaScript
     /
    public function setScriptSrc(array $sources, bool $allowInline = false): self {
        $scriptSources = $sources;

        if ($allowInline) {
            $scriptSources[] = "'nonce-{$this->nonce}'";
            // Alternative : 'unsafe-inline' (déconseillé)
        }

        $this->directives['script-src'] = $scriptSources;
        return $this;
    }

    /
      Configure les sources CSS
     /
    public function setStyleSrc(array $sources, bool $allowInline = false): self {
        $styleSources = $sources;

        if ($allowInline) {
            $styleSources[] = "'nonce-{$this->nonce}'";
        }

        $this->directives['style-src'] = $styleSources;
        return $this;
    }

    /
      Configure les sources d'images
     /
    public function setImgSrc(array $sources): self {
        $this->directives['img-src'] = $sources;
        return $this;
    }

    /
      Configure les sources de fonts
     /
    public function setFontSrc(array $sources): self {
        $this->directives['font-src'] = $sources;
        return $this;
    }

    /
      Configure les endpoints de connexion (fetch, XHR, WebSocket)
     /
    public function setConnectSrc(array $sources): self {
        $this->directives['connect-src'] = $sources;
        return $this;
    }

    /
      Empêche le chargement en iframe
     /
    public function disableFraming(): self {
        $this->directives['frame-ancestors'] = ["'none'"];
        return $this;
    }

    /
      Configure l'URL de reporting des violations
     /
    public function setReportUri(string $uri): self {
        $this->directives['report-uri'] = [$uri];
        return $this;
    }

    /
      Active le mode report-only (test sans bloquer)
     /
    public function setReportOnly(bool $reportOnly = true): self {
        $this->reportOnly = $reportOnly;
        return $this;
    }

    /
      Récupère le nonce pour utilisation dans les templates
     /
    public function getNonce(): string {
        return $this->nonce;
    }

    /
      Construit et envoie le header CSP
     /
    public function build(): string {
        $cspParts = [];

        foreach ($this->directives as $directive => $sources) {
            $cspParts[] = $directive . ' ' . implode(' ', $sources);
        }

        $csp = implode('; ', $cspParts);

        $headerName = $this->reportOnly
            ? 'Content-Security-Policy-Report-Only'
            : 'Content-Security-Policy';

        header("{$headerName}: {$csp}", true);

        return $csp;
    }
}

// Configuration pour une application moderne
$csp = new CSPBuilder();
$csp
    ->setDefaultSrc(["'self'"])
    ->setScriptSrc([
        "'self'",
        'https://cdn.jsdelivr.net',
        'https://www.google-analytics.com'
    ], true) // allowInline = true pour utiliser le nonce
    ->setStyleSrc([
        "'self'",
        'https://fonts.googleapis.com'
    ], true)
    ->setImgSrc([
        "'self'",
        'data:',
        'https:',
        'https://www.google-analytics.com'
    ])
    ->setFontSrc([
        "'self'",
        'https://fonts.gstatic.com'
    ])
    ->setConnectSrc([
        "'self'",
        'https://api.example.com',
        'wss://websocket.example.com'
    ])
    ->disableFraming()
    ->setReportUri('/csp-report-endpoint')
    ->build();

// Utilisation du nonce dans les templates
define('CSPNONCE', $csp->getNonce());

Utilisation du Nonce dans les Templates




    
    Application Sécurisée

    
    

    
    


    # Contenu Sécurisé

    
    

    
    

    
    



Endpoint de Rapport CSP


  Endpoint pour recevoir les rapports de violation CSP
 /
class CSPReportHandler {
    private PDO $pdo;
    private string $logFile;

    public function construct(PDO $pdo, string $logFile = '/var/log/csp-violations.log') {
        $this->pdo = $pdo;
        $this->logFile = $logFile;
    }

    /
      Traite les rapports de violation CSP
     /
    public function handleReport(): void {
        // Lire le JSON du corps de la requête
        $json = filegetcontents('php://input');

        if (empty($json)) {
            httpresponsecode(400);
            return;
        }

        $report = jsondecode($json, true);

        if (jsonlasterror() !== JSONERRORNONE || !isset($report['csp-report'])) {
            httpresponsecode(400);
            return;
        }

        $violation = $report['csp-report'];

        // Stocker en base de données
        $this->storeViolation($violation);

        // Logger dans un fichier
        $this->logViolation($violation);

        // Alerter si violation critique
        if ($this->isCriticalViolation($violation)) {
            $this->alertSecurity($violation);
        }

        httpresponsecode(204);
    }

    /
      Stocke la violation en base de données
     /
    private function storeViolation(array $violation): void {
        $stmt = $this->pdo->prepare(
            "INSERT INTO cspviolations
             (documenturi, blockeduri, violateddirective, sourcefile, linenumber, useragent, ip, createdat)
             VALUES (?, ?, ?, ?, ?, ?, ?, NOW())"
        );

        $stmt->execute([
            $violation['document-uri'] ?? null,
            $violation['blocked-uri'] ?? null,
            $violation['violated-directive'] ?? null,
            $violation['source-file'] ?? null,
            $violation['line-number'] ?? null,
            $SERVER['HTTPUSERAGENT'] ?? null,
            $SERVER['REMOTEADDR'] ?? null
        ]);
    }

    /
      Logger la violation
     /
    private function logViolation(array $violation): void {
        $logEntry = sprintf(
            "[%s] CSP Violation: %s blocked %s on %s (User-Agent: %s, IP: %s)n",
            date('Y-m-d H:i:s'),
            $violation['violated-directive'] ?? 'unknown',
            $violation['blocked-uri'] ?? 'unknown',
            $violation['document-uri'] ?? 'unknown',
            $SERVER['HTTPUSERAGENT'] ?? 'unknown',
            $SERVER['REMOTEADDR'] ?? 'unknown'
        );

        errorlog($logEntry, 3, $this->logFile);
    }

    /
      Détermine si une violation est critique
     /
    private function isCriticalViolation(array $violation): bool {
        $directive = $violation['violated-directive'] ?? '';

        // Les violations script-src sont critiques (potentiel XSS)
        if (strpos($directive, 'script-src') !== false) {
            return true;
        }

        // Vérifier si c'est une tentative d'injection
        $blockedUri = $violation['blocked-uri'] ?? '';
        if (strpos($blockedUri, 'data:') === 0 || strpos($blockedUri, 'blob:') === 0) {
            return true;
        }

        return false;
    }

    /
      Alerte l'équipe de sécurité
     /
    private function alertSecurity(array $violation): void {
        // Implémenter notification (email, Slack, PagerDuty, etc.)
        $message = "CSP Critical Violation Detected:n" . jsonencode($violation, JSONPRETTYPRINT);

        // Exemple : envoyer un email
        mail(
            'security@example.com',
            'CSP Critical Violation',
            $message,
            'From: csp-monitor@example.com'
        );
    }
}

// Route pour recevoir les rapports
// /csp-report-endpoint
if ($SERVER['REQUESTURI'] === '/csp-report-endpoint') {
    $handler = new CSPReportHandler($pdo);
    $handler->handleReport();
    exit;
}

CORS : Cross-Origin Resource Sharing

Comprendre CORS

CORS permet à une application web d’accéder à des ressources d’une autre origine (domaine, protocole ou port différent). Sans configuration CORS, les navigateurs bloquent ces requêtes par sécurité.

Scénarios CORS

# Même origine (pas de CORS nécessaire)
https://example.com/api  →  https://example.com/page
✅ Autorisé

# Origines différentes (CORS requis)
https://app.example.com  →  https://api.example.com
❌ Bloqué sans CORS

https://example.com      →  https://example.org
❌ Bloqué sans CORS

http://example.com       →  https://example.com  (protocole différent)
❌ Bloqué sans CORS

Configuration CORS Sécurisée


  Gestionnaire CORS configuré
 /
class CORSManager {
    private array $allowedOrigins = [];
    private array $allowedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'];
    private array $allowedHeaders = ['Content-Type', 'Authorization', 'X-Requested-With'];
    private bool $allowCredentials = false;
    private int $maxAge = 3600; // Cache preflight 1h

    /
      Configure les origines autorisées
     /
    public function setAllowedOrigins(array $origins): self {
        $this->allowedOrigins = $origins;
        return $this;
    }

    /
      Configure les méthodes HTTP autorisées
     /
    public function setAllowedMethods(array $methods): self {
        $this->allowedMethods = $methods;
        return $this;
    }

    /
      Configure les headers autorisés
     /
    public function setAllowedHeaders(array $headers): self {
        $this->allowedHeaders = $headers;
        return $this;
    }

    /
      Active/désactive les credentials (cookies, auth)
     /
    public function setAllowCredentials(bool $allow): self {
        $this->allowCredentials = $allow;
        return $this;
    }

    /
      Applique les headers CORS
     /
    public function handleCORS(): void {
        $origin = $SERVER['HTTPORIGIN'] ?? '';

        // Vérifier si l'origine est autorisée
        if (!$this->isOriginAllowed($origin)) {
            // Ne pas envoyer de headers CORS si origine non autorisée
            // Cela bloquera la requête côté navigateur
            return;
        }

        // Access-Control-Allow-Origin
        header("Access-Control-Allow-Origin: {$origin}", true);

        // Access-Control-Allow-Credentials
        if ($this->allowCredentials) {
            header('Access-Control-Allow-Credentials: true', true);
        }

        // Traiter requête preflight (OPTIONS)
        if ($SERVER['REQUESTMETHOD'] === 'OPTIONS') {
            $this->handlePreflight();
            exit(0);
        }
    }

    /
      Vérifie si une origine est autorisée
     /
    private function isOriginAllowed(string $origin): bool {
        if (empty($origin)) {
            return false;
        }

        // Wildcard (ATTENTION : dangereux en production)
        if (inarray('', $this->allowedOrigins)) {
            return true;
        }

        // Vérification exacte
        if (inarray($origin, $this->allowedOrigins)) {
            return true;
        }

        // Vérification par pattern (ex: .example.com)
        foreach ($this->allowedOrigins as $allowed) {
            if ($this->matchOriginPattern($origin, $allowed)) {
                return true;
            }
        }

        return false;
    }

    /
      Matche un pattern d'origine (support wildcards)
     /
    private function matchOriginPattern(string $origin, string $pattern): bool {
        // Convertir le pattern en regex
        $regex = '/^' . strreplace(
            ['', '.'],
            ['[^.]+', '.'],
            $pattern
        ) . '$/';

        return pregmatch($regex, $origin) === 1;
    }

    /
      Traite une requête preflight (OPTIONS)
     /
    private function handlePreflight(): void {
        // Access-Control-Allow-Methods
        header(
            'Access-Control-Allow-Methods: ' . implode(', ', $this->allowedMethods),
            true
        );

        // Access-Control-Allow-Headers
        header(
            'Access-Control-Allow-Headers: ' . implode(', ', $this->allowedHeaders),
            true
        );

        // Access-Control-Max-Age
        header("Access-Control-Max-Age: {$this->maxAge}", true);

        httpresponsecode(204);
    }
}

// Configuration pour une API
$cors = new CORSManager();
$cors
    ->setAllowedOrigins([
        'https://app.example.com',
        'https://admin.example.com',
        'https://.example.com' // Tous les sous-domaines
    ])
    ->setAllowedMethods(['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'])
    ->setAllowedHeaders(['Content-Type', 'Authorization', 'X-API-Key'])
    ->setAllowCredentials(true) // Autoriser cookies/auth
    ->handleCORS();

CORS : Configurations par Cas d’Usage

setAllowedOrigins(['']) // Toutes origines
    ->setAllowedMethods(['GET', 'OPTIONS'])
    ->setAllowCredentials(false)
    ->handleCORS();

// Cas 2 : API privée (authentification requise)
$privateAPICors = new CORSManager();
$privateAPICors
    ->setAllowedOrigins(['https://app.example.com'])
    ->setAllowedMethods(['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'])
    ->setAllowedHeaders(['Content-Type', 'Authorization'])
    ->setAllowCredentials(true) // Pour envoyer cookies de session
    ->handleCORS();

// Cas 3 : Développement local
if ($ENV['APPENV'] === 'development') {
    $devCors = new CORSManager();
    $devCors
        ->setAllowedOrigins([
            'http://localhost:3000',
            'http://localhost:5173', // Vite
            'http://127.0.0.1:3000'
        ])
        ->setAllowedMethods(['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'])
        ->setAllowCredentials(true)
        ->handleCORS();
}

Cas d’Étude : Sécurisation d’une SPA + API

Contexte

Une Single Page Application (Vue.js) consommant une API REST PHP doit être sécurisée contre XSS, CSRF et injections.

Architecture

Frontend (SPA)          Backend (API)
https://app.example.com → https://api.example.com

Configuration Complète


  Configuration de sécurité complète pour API
 /
class APISecurityConfig {
    private SecurityHeadersManager $headers;
    private CORSManager $cors;
    private CSPBuilder $csp;

    public function construct() {
        $this->initializeHeaders();
        $this->initializeCORS();
        $this->initializeCSP();
    }

    private function initializeHeaders(): void {
        $this->headers = new SecurityHeadersManager('production');
    }

    private function initializeCORS(): void {
        $this->cors = new CORSManager();
        $this->cors
            ->setAllowedOrigins([
                'https://app.example.com',
                'https://admin.example.com'
            ])
            ->setAllowedMethods(['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'])
            ->setAllowedHeaders([
                'Content-Type',
                'Authorization',
                'X-CSRF-Token'
            ])
            ->setAllowCredentials(true);
    }

    private function initializeCSP(): void {
        $this->csp = new CSPBuilder();
        $this->csp
            ->setDefaultSrc(["'self'"])
            ->setScriptSrc(["'self'"], false) // Pas de inline scripts
            ->setStyleSrc(["'self'"], false)
            ->setConnectSrc(["'self'"])
            ->disableFraming();
    }

    /
      Applique toute la configuration de sécurité
     /
    public function apply(): void {
        $this->headers->applySecurityHeaders();
        $this->cors->handleCORS();
        $this->csp->build();
    }
}

// Appliquer la configuration
$security = new APISecurityConfig();
$security->apply();

Tests de Sécurité

#!/bin/bash
# test-security-headers.sh

URL="https://api.example.com"

echo "=== Test des Headers de Sécurité ==="
echo

# Tester HSTS
echo "1. Strict-Transport-Security:"
curl -sI "$URL" | grep -i "strict-transport-security"
echo

# Tester X-Frame-Options
echo "2. X-Frame-Options:"
curl -sI "$URL" | grep -i "x-frame-options"
echo

# Tester CSP
echo "3. Content-Security-Policy:"
curl -sI "$URL" | grep -i "content-security-policy"
echo

# Tester CORS
echo "4. CORS (requête OPTIONS):"
curl -X OPTIONS "$URL/api/users" 
  -H "Origin: https://app.example.com" 
  -H "Access-Control-Request-Method: GET" 
  -i | grep -i "access-control"
echo

# Tester X-Content-Type-Options
echo "5. X-Content-Type-Options:"
curl -sI "$URL" | grep -i "x-content-type-options"
echo

echo "=== Test terminé ==="

Ressources et Outils

Outils de Validation

  • SecurityHeaders.com : Scanner de headers de sécurité
  • Mozilla Observatory : Audit de sécurité web complet
  • CSP Evaluator (Google) : Validateur de CSP
  • CORS Tester : Tester les configurations CORS
  • Checklist de Sécurité Headers

# Checklist Headers de Sécurité

## Configuration Serveur
  • [ ] HTTPS activé (TLS 1.3)
  • [ ] Certificat SSL valide
  • [ ] Redirection HTTP vers HTTPS
  • [ ] HSTS configuré (max-age 2 ans minimum)
  • ## Headers Essentiels
  • [ ] Strict-Transport-Security présent
  • [ ] X-Frame-Options configuré (DENY ou SAMEORIGIN)
  • [ ] X-Content-Type-Options: nosniff
  • [ ] Referrer-Policy défini
  • [ ] Permissions-Policy configuré
  • ## Content Security Policy
  • [ ] CSP défini (pas de 'unsafe-inline' ou 'unsafe-eval')
  • [ ] Nonces utilisés pour scripts/styles inline
  • [ ] Endpoint de rapport CSP configuré
  • [ ] CSP testé en mode report-only d'abord
  • ## CORS (si applicable)
  • [ ] Origines autorisées définies (pas de wildcard)
  • [ ] Méthodes HTTP limitées au nécessaire
  • [ ] Headers autorisés minimaux
  • [ ] Credentials correctement configurés
  • ## Autres
  • [ ] Headers sensibles supprimés (X-Powered-By, Server)
  • [ ] Cache-Control configuré pour pages sensibles
  • [ ] Tests automatisés dans CI/CD
  • Conclusion

    Les headers de sécurité, CORS et CSP forment un bouclier essentiel contre les attaques web modernes. Bien configurés, ils peuvent prévenir la majorité des vulnérabilités côté client sans impacter les performances.

    Points Clés

  • HTTPS d’abord : Aucun header de sécurité ne remplace une connexion chiffrée
  • CSP strict : Évitez ‘unsafe-inline’ et ‘unsafe-eval’
  • CORS restrictif : Whitelistez uniquement les origines nécessaires
  • Testez progressivement : Utilisez report-only avant de bloquer
  • Automatisez les tests* : Intégrez la validation dans votre CI/CD
  • Une configuration de sécurité robuste est un processus itératif. Commencez par les headers essentiels, puis raffinez progressivement votre CSP et CORS en fonction de vos besoins réels.

    Une remarque, un retour ?

    Cet article est vivant — corrections, contre-arguments et retours de production sont les bienvenus. Trois canaux, choisissez celui qui vous convient.