Retour au cours

infra / nginx

Headers de sécurité

Leçon 101 exercice

Explication

Ce que vous allez apprendre

  • Ajouter les headers de sécurité essentiels : HSTS, X-Frame-Options, X-Content-Type-Options
  • Comprendre pourquoi add_header ... always; est nécessaire pour les réponses d'erreur
  • Écrire une Content-Security-Policy (CSP) de base et comprendre son risque principal
  • Masquer la version de Nginx exposée par défaut avec server_tokens off
  • Bloquer l'accès aux fichiers sensibles souvent oubliés en production (.git, .env)

Dans quel contexte ?

Un audit de sécurité externe pointe qu'un site en production ne renvoie aucun header de sécurité HTTP : aucune protection contre le clickjacking (afficher le site dans une iframe malveillante pour piéger un clic), aucune protection contre le sniffing MIME, et la version exacte de Nginx est visible dans chaque réponse, facilitant la recherche de vulnérabilités connues par un attaquant.

D'abord, une poignée de headers forment le socle minimal

HSTS force le navigateur à toujours utiliser HTTPS pour ce domaine, même si un lien http:// est tapé à la main ou cliqué depuis un vieux favori. X-Frame-Options empêche l'affichage du site dans une <iframe> d'un autre domaine, une protection directe contre le clickjacking. X-Content-Type-Options empêche le navigateur de "deviner" un type de fichier différent de celui déclaré par le serveur, ce qui limite certaines classes d'attaques XSS.

Une fois ces headers ajoutés, un détail syntaxique compte énormément

Sans le mot-clé always à la fin de la directive add_header, le header n'est envoyé QUE sur les réponses de succès (2xx, 3xx) — pas sur les réponses d'erreur (4xx, 5xx). Or, une page d'erreur reste une cible potentielle pour du clickjacking ou du sniffing MIME : always garantit que la protection s'applique dans tous les cas.

HeaderProtège contreNécessite always
Strict-Transport-SecurityDowngrade HTTP après un lien tapé en clairOui
X-Frame-OptionsClickjacking (iframe malveillante)Oui
X-Content-Type-OptionsSniffing MIMEOui
Content-Security-PolicyXSS (scripts injectés)Oui

Ensuite, le header le plus puissant est aussi le plus délicat à régler

La Content-Security-Policy (CSP) restreint précisément quelles sources de scripts, styles et images le navigateur a le droit de charger. C'est la protection la plus efficace contre le XSS, mais une règle trop stricte casse silencieusement des fonctionnalités du site : aucune erreur n'apparaît dans les logs Nginx, tout se passe dans la console du navigateur, invisible sans inspection manuelle.

Piège fréquent

Déployer directement une CSP stricte en production, sans phase de test, risque de casser des scripts tiers légitimes (widgets de paiement, analytics) sans que personne ne s'en aperçoive avant que des utilisateurs ne signalent des dysfonctionnements. Teste toujours d'abord en mode Content-Security-Policy-Report-Only, qui journalise les violations sans bloquer réellement le contenu.

Prérequis

Cette leçon suppose une compréhension de base des attaques XSS et clickjacking : sans ce contexte, ces headers peuvent sembler être une checklist arbitraire plutôt que des protections ciblées contre des menaces précises.

Il reste deux dernières mesures simples mais efficaces

server_tokens off; masque la version exacte de Nginx dans les réponses, réduisant l'information disponible pour un attaquant en reconnaissance. Bloquer explicitement l'accès aux fichiers sensibles souvent oubliés — .git, .env, fichiers .bak ou .sql — avec un return 404 (plutôt qu'un 403, qui révèle que le fichier existe bel et bien) referme des portes d'entrée classiques.

Bonne pratique

Vérifie régulièrement les headers effectivement renvoyés par ton site avec curl -sI : une configuration correcte au moment de sa création peut se perdre après une modification ultérieure d'un server block, sans que personne ne le remarque avant un audit de sécurité.

Maintenant que la posture de sécurité HTTP est solide, la prochaine leçon s'intéresse à un aspect opérationnel tout aussi essentiel : les logs, leur format et leur exploitation pour diagnostiquer un problème en production.

Commandes & code

Headers de sécurité

nginx
server {
    listen 443 ssl;
    server_name example.com;

    # HSTS : force le navigateur à toujours utiliser HTTPS pour ce domaine, même sur un lien http:// tapé à la main
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # Empêche l'affichage du site dans une <iframe> d'un autre domaine (protection clickjacking)
    add_header X-Frame-Options "SAMEORIGIN" always;

    # Empêche le navigateur de "deviner" un type MIME différent de celui déclaré (protection contre certains XSS)
    add_header X-Content-Type-Options "nosniff" always;

    # Contrôle les infos envoyées dans le header Referer lors d'une navigation sortante
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Désactive des API navigateur sensibles par défaut (caméra, micro, géoloc) sauf besoin explicite
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
}
nginx
# Content-Security-Policy : la protection la plus puissante contre le XSS, aussi la plus délicate à régler
server {
    add_header Content-Security-Policy "
        default-src 'self';
        script-src 'self' https://cdn.example.com;
        style-src 'self' 'unsafe-inline';
        img-src 'self' data: https:;
        connect-src 'self' https://api.example.com;
        frame-ancestors 'self';
        base-uri 'self';
        form-action 'self';
    " always;
    # ATTENTION : une CSP trop stricte casse le site en silence (rien dans les logs Nginx, tout dans la console navigateur)
}
nginx
# Masquer la version de Nginx exposée par défaut dans les headers et pages d'erreur
http {
    server_tokens off;    # remplace "Server: nginx/1.24.0" par juste "Server: nginx"
}
nginx
# Bloquer l'accès aux fichiers sensibles souvent oubliés en prod
server {
    location ~ /\.(git|env|htaccess) {
        deny all;
        return 404;         # 404 plutôt que 403 : ne révèle même pas que le fichier existe
    }

    location ~* \.(bak|sql|log|conf)$ {
        deny all;
        return 404;
    }
}
bash
# Vérifier les headers effectivement renvoyés
curl -sI https://example.com | grep -Ei "strict-transport|x-frame|x-content-type|content-security|referrer-policy"

Résumé

  • always sur add_header est nécessaire pour que le header soit envoyé même sur les réponses d'erreur (4xx/5xx).
  • HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy : le socle minimal à mettre partout.
  • CSP est le plus protecteur contre le XSS mais casse silencieusement le site si mal configuré : tester en mode Content-Security-Policy-Report-Only d'abord.
  • server_tokens off + blocage des fichiers sensibles (.git, .env, .bak) réduisent la surface de reconnaissance d'un attaquant.

Exercices pratiques

1 disponible
1

Mission : corriger les manques révélés par un audit de sécurité HTTP

Objectif : Ajouter les headers de sécurité essentiels avec la portée correcte et fermer les fichiers sensibles exposés par erreur.

Contexte

Un audit externe constate trois manques sur example.com : aucun header de sécurité HTTP n'est renvoyé (même pas sur les pages d'erreur 4xx), la version exacte de Nginx apparaît dans chaque réponse, et .git/config est accessible publiquement via l'URL.

Résoudre l’exercice →