Aller au contenu
Narvy

En-têtes de sécurité HTTP : quoi envoyer, quoi retirer, comment vérifier

CSP, HSTS, nosniff, frame-ancestors, Referrer-Policy et attributs de cookies, avec les valeurs recommandées par l'OWASP, deux pièges de nginx et une vérification avec curl.

Narvy6 min de lecture

Les en-têtes de sécurité indiquent au navigateur ce qu'une page a le droit de charger, si un autre site peut l'afficher dans un cadre et si elle doit être consultée uniquement en HTTPS. Ils tiennent en quelques lignes de configuration serveur. La référence utilisée dans cet article est l'OWASP HTTP Security Response Headers Cheat Sheet, complétée pour la CSP par le guide de Google sur une Content Security Policy stricte.

Les en-têtes à envoyer

En-tête Valeur recommandée par l'OWASP Effet
Strict-Transport-Security max-age=63072000; includeSubDomains; preload Le navigateur n'utilise plus que HTTPS pour le site. Lisez la section sur le déploiement avant de copier cette valeur.
Content-Security-Policy une politique stricte à nonce (voir plus bas) Limite les scripts et ressources qui peuvent s'exécuter ou se charger.
X-Content-Type-Options nosniff Empêche le navigateur de deviner un type MIME différent du Content-Type.
X-Frame-Options DENY Bloque l'affichage dans un cadre (clickjacking). La directive CSP frame-ancestors le remplace dans les navigateurs qui la prennent en charge.
Referrer-Policy strict-origin-when-cross-origin N'envoie que l'origine aux autres sites. C'est le comportement par défaut des navigateurs récents ; l'OWASP recommande de le fixer explicitement.
Permissions-Policy geolocation=(), camera=(), microphone=() Désactive les fonctions du navigateur dont le site n'a pas besoin, même si une XSS tente de les activer.
Cross-Origin-Opener-Policy same-origin Tient les documents d'autres origines hors du groupe de contextes de navigation de la page.
Cache-Control no-store sur les réponses sensibles Interdit le stockage dans les caches HTTP conformes. no-cache autorise le stockage, avec revalidation.

Déclarez le Content-Type des pages HTML avec un jeu de caractères (text/html; charset=UTF-8). L'OWASP précise que le charset est nécessaire pour éviter des XSS dans les pages HTML.

Les en-têtes à retirer

  • X-XSS-Protection : l'OWASP recommande de ne pas l'envoyer, ou d'envoyer X-XSS-Protection: 0. Dans certains cas, le filtre qu'il active crée des failles XSS sur des sites qui n'en avaient pas.
  • Expect-CT : à ne pas utiliser.
  • Public-Key-Pins : retiré de Chromium en 2018 et ignoré par les navigateurs récents. À supprimer de la production.
  • Server et X-Powered-By : à supprimer, ou à remplir avec une valeur qui ne nomme ni logiciel ni version.

Content Security Policy : stricte, puis appliquée

Les premières CSP étaient des listes de domaines autorisés. La cheat sheet CSP de l'OWASP recommande désormais une CSP stricte, fondée sur un nonce ou une empreinte, qu'elle décrit comme plus simple à déployer et plus difficile à contourner. La version à nonce, telle que la donne le guide de Google :

Content-Security-Policy:
  script-src 'nonce-{RANDOM}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';

Le nonce est une nouvelle valeur aléatoire à chaque réponse. C'est l'application qui doit le générer, et non le serveur web, parce que la même valeur doit figurer dans l'en-tête et sur chaque balise <script nonce="..."> de la page. strict-dynamic autorise les scripts chargés par un script de confiance, ce qui laisse fonctionner la plupart des bibliothèques tierces. object-src 'none' désactive les plugins et base-uri 'none' bloque l'injection de balises <base>.

Ajoutez frame-ancestors 'none' (ou la liste des origines autorisées à vous afficher dans un cadre) au même en-tête pour remplacer X-Frame-Options.

Pour déployer sans casser de pages, envoyez d'abord la politique en Content-Security-Policy-Report-Only. Le navigateur signale ce qu'il aurait bloqué et ne bloque rien. L'OWASP précise que les deux en-têtes peuvent coexister, par exemple une politique stricte en report-only à côté d'une politique plus souple appliquée. Passez à l'en-tête bloquant quand les rapports sont propres.

Deux limites rappelées par la cheat sheet CSP de l'OWASP : une CSP déclarée dans une balise <meta> ne permet ni la protection contre l'affichage en cadre, ni le sandboxing, ni l'envoi de rapports ; et la CSP reste une seconde couche. Elle ne remplace ni l'encodage des sorties ni le reste de la prévention des XSS.

HSTS sans bloquer ses utilisateurs

HSTS demande aux navigateurs de refuser le HTTP en clair pour votre domaine. Une erreur dure aussi longtemps que le max-age. La cheat sheet HSTS de l'OWASP donne des valeurs d'exemple qui, prises dans l'ordre, forment un déploiement prudent :

  1. Commencer par une durée courte pendant le déploiement : max-age=86400; includeSubDomains.
  2. N'ajouter includeSubDomains que si tous les sous-domaines, actuels et futurs, sont servis en HTTPS.
  3. Passer à max-age=63072000 (deux ans) une fois la configuration stable.
  4. N'ajouter preload qu'après avoir lu les règles de retrait de la liste de préchargement. L'OWASP prévient que ce choix peut avoir des conséquences permanentes. Le drapeau ne vaut que consentement : il faut ensuite soumettre le domaine à la liste.

La cheat sheet principale ajoute une mise en garde : avec un max-age long, un certificat expiré ou révoqué peut empêcher des utilisateurs légitimes d'accéder au site jusqu'à la fin de la durée.

Deux pièges de nginx

La directive nginx add_header a deux comportements qui font disparaître des en-têtes sans prévenir.

Les codes de statut. Sans le paramètre always, nginx n'ajoute l'en-tête qu'aux réponses de code 200, 201, 204, 206, 301, 302, 303, 304, 307 et 308. Vos pages 404 et 500 partent sans.

L'héritage. Les directives add_header ne sont héritées du niveau supérieur que si le niveau courant n'en définit aucune. Un seul add_header dans un bloc location supprime, pour cette location, tous les en-têtes définis au niveau server. Depuis nginx 1.29.3, add_header_inherit merge; change ce comportement ; sur les versions antérieures, répétez les en-têtes ou placez-les dans un fichier inclus avec include dans chaque bloc.

Un bloc au niveau server qui évite les deux pièges :

add_header Strict-Transport-Security "max-age=86400; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;

La CSP reste dans l'application, puisqu'elle porte le nonce propre à chaque réponse.

Cookies

Set-Cookie n'est pas un en-tête de sécurité, mais ses attributs le sont. La Session Management Cheat Sheet de l'OWASP rend Secure et HttpOnly obligatoires sur les cookies de session et exige un SameSite=Strict (de préférence) ou SameSite=Lax explicite. Elle présente SameSite comme une défense en profondeur contre le CSRF, qui ne remplace pas un jeton CSRF.

Set-Cookie: session=...; Secure; HttpOnly; SameSite=Strict; Path=/

Vérifier ce que le serveur envoie

Contrôlez une page normale, une page d'erreur et une réponse d'API :

curl -sI https://example.com/
curl -sI https://example.com/cette-page-n-existe-pas
curl -sI https://example.com/api/health

Si les en-têtes apparaissent sur la première réponse et pas sur la 404, il manque un always. Pour les API JSON, l'OWASP rappelle que la CSP et X-Frame-Options n'apportent rien sur une réponse que le navigateur n'affiche pas ; nosniff et un Content-Type correct restent utiles.

Sources officielles

  1. OWASP HTTP Security Response Headers Cheat Sheetcheatsheetseries.owasp.org
  2. OWASP Content Security Policy Cheat Sheetcheatsheetseries.owasp.org
  3. web.dev (Google) : Mitigate XSS with a strict Content Security Policyweb.dev
  4. OWASP HTTP Strict Transport Security Cheat Sheetcheatsheetseries.owasp.org
  5. OWASP Session Management Cheat Sheetcheatsheetseries.owasp.org
  6. Documentation nginx : ngx_http_headers_modulenginx.org