Bien définir les en-têtes de sécurité dans nginx et Apache

Quelques lignes dans la configuration du serveur protègent contre le clickjacking, le MIME sniffing et bien des conséquences du XSS. L’essentiel est qu’elles arrivent vraiment avec chaque réponse.

Quels en-têtes sont utiles

X-XSS-Protection n’est plus nécessaire. Les navigateurs modernes l’ignorent, et dans les anciens il pouvait même nuire.

nginx

# /etc/nginx/snippets/security-headers.conf
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" always;

# dans le bloc server :
include snippets/security-headers.conf;
server_tokens off;

nginx a deux pièges :

Apache

# sudo a2enmod headers
<VirtualHost *:443>
    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
    Header always set X-Content-Type-Options "nosniff"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
    Header always set Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
    Header always unset X-Powered-By
</VirtualHost>

# global, p. ex. dans security.conf
ServerTokens Prod
ServerSignature Off

Sur un hébergement mutualisé sans accès à la configuration du serveur, les mêmes lignes Header fonctionnent en général dans le .htaccess à la racine. La version de PHP dans l’en-tête X-Powered-By se désactive avec expose_php = Off dans php.ini.

Mettre en place la CSP sans casser le site

Une CSP trop stricte bloque tes propres scripts, les vidéos intégrées ou les outils d’analyse. Procède donc par étapes :

  1. D’abord en Content-Security-Policy-Report-Only. Le navigateur ne bloque rien mais signale les violations dans la console.
  2. Parcourir la console sur les pages importantes et ajouter les sources nécessaires, p. ex. script-src 'self' https://cdn.example.net.
  3. Déplacer les scripts en ligne dans des fichiers ou leur donner un nonce. 'unsafe-inline' dans script-src annule en grande partie la protection contre le XSS.
  4. Seulement ensuite passer à Content-Security-Policy.

Prudence avec HSTS

HSTS ne se retire pas facilement : les navigateurs le retiennent pour la durée indiquée. Commence avec max-age=300, vérifie que tous les sous-domaines gèrent vraiment HTTPS, puis passe à deux ans. includeSubDomains concerne aussi des noms internes comme intranet.example.com s’ils sont sous le même domaine.

Vérifier

curl -sI https://example.com
curl -sI https://example.com/nexiste-pas   # aussi en 404 ?

La vérification des en-têtes de sécurité évalue tous les en-têtes, repère les erreurs typiques comme 'unsafe-inline' et génère les lignes adaptées à ton serveur.

FAQ

X-Frame-Options suffit-il ou faut-il frame-ancestors ?

frame-ancestors dans la CSP est la méthode moderne et permet plusieurs sources. Pour de très vieux navigateurs, ajouter X-Frame-Options: SAMEORIGIN ne nuit pas.

Pourquoi vois-je les en-têtes en double ?

En général, l’application (p. ex. PHP ou un CMS) et le serveur web les définissent tous les deux. Deux CSP sont appliquées toutes les deux, la plus stricte l’emporte. Mieux vaut les définir à un seul endroit.

Les API ont-elles besoin d’en-têtes de sécurité ?

Oui, au moins HSTS, nosniff et une CSP stricte comme default-src 'none'; frame-ancestors 'none'. Ils ne coûtent rien et empêchent l’abus des réponses dans le navigateur.

Un CDN définit-il les en-têtes pour moi ?

Seulement si tu le configures. Beaucoup de CDN transmettent simplement les en-têtes du serveur d’origine.