Ce que font les en-têtes de sécurité, et ce qu'ils ne font pas
Un en-tête de sécurité est un en-tête de réponse qui demande au navigateur d'être plus strict avec votre page qu'il ne le serait par défaut : utiliser uniquement HTTPS, ne plus deviner les types de fichiers, rester hors des cadres d'autres sites, partager moins dans le Referer, laisser la caméra éteinte et n'exécuter que les scripts que vous avez approuvés.
C'est de la défense en profondeur. Aucun ne corrige une application vulnérable : une injection SQL ou une connexion défaillante reste tout aussi défaillante avec un jeu d'en-têtes parfait. Ils limitent les dégâts des failles qu'un navigateur peut contenir, comme le cross-site scripting, le clickjacking, la rétrogradation de protocole et les fuites de données par le référent, pour chaque visiteur, au prix de quelques centaines d'octets.
Ils ne fonctionnent en outre que dans les navigateurs. Un client d'API, un robot ou le script d'un attaquant les ignore tous ; c'est pourquoi un WAF et le code de l'application restent la première ligne de défense.
Strict-Transport-Security (HSTS)
Strict-Transport-Security demande au navigateur d'utiliser HTTPS pour votre nom d'hôte et de s'en souvenir pendant max-age secondes, si bien que même une adresse http:// tapée à la main ne quitte jamais l'appareil en clair. C'est l'en-tête qui a le plus d'effet pour le moins d'effort, à condition que chaque URL du site fonctionne déjà en HTTPS.
Un départ sûr est max-age=31536000 : un an, pour ce nom d'hôte uniquement. includeSubDomains et preload vont plus loin et sont difficiles à annuler. Le guide HSTS explique à quoi chacun vous engage, comment vérifier l'en-tête et comment revenir en arrière.
Sur CDN.com.tr, HSTS est un préréglage du compte dans les Règles de diffusion : aucune ligne d'en-tête à écrire. Il envoie exactement max-age=31536000 sur les réponses HTTPS et rien en HTTP simple.
X-Content-Type-Options: nosniff
Les navigateurs devinaient autrefois le type d'un fichier d'après son contenu quand l'en-tête Content-Type semblait faux. Cette devinette, le MIME sniffing, permettait à un fichier envoyé qui ressemblait à un script d'être exécuté comme tel. X-Content-Type-Options: nosniff la désactive : une feuille de style doit arriver en text/css et un script avec un type JavaScript, sinon le navigateur la refuse.
nosniff est la seule valeur, et l'en-tête est sans risque sur toute réponse, HTML ou non. Vérifiez d'abord une seule chose : que votre serveur étiquette correctement les fichiers. Un script servi en text/plain cesse de se charger dès que l'en-tête est en place, et c'est exactement le comportement que vous demandez.
X-Frame-Options et frame-ancestors : qui peut encadrer vos pages
Le clickjacking charge votre page dans un cadre invisible sur un autre site et aligne l'un de ses boutons sur l'un des vôtres : le visiteur clique sur « Supprimer le compte » en croyant cliquer sur « Lecture ». La parade consiste à dire au navigateur qui peut placer votre page dans un cadre.
X-Frame-Options a deux valeurs utiles : DENY pour personne et SAMEORIGIN pour les pages de votre propre origine. L'ancienne valeur ALLOW-FROM est ignorée par les navigateurs actuels. Pour autoriser une liste de sites, utilisez la directive CSP frame-ancestors, par exemple frame-ancestors 'self' https://partner.example.
Quand une réponse porte les deux, les navigateurs qui comprennent frame-ancestors la suivent et ignorent X-Frame-Options. Envoyer les deux est courant et sans danger, l'ancien en-tête couvrant les anciens navigateurs, tant qu'ils disent la même chose. Une limite : frame-ancestors ne fonctionne qu'en en-tête HTTP, jamais dans une balise <meta>.
Referrer-Policy : ce qui part à chaque clic
Quand un visiteur suit un lien, ou que votre page charge une image, un script ou une police depuis un autre site, le navigateur peut envoyer l'adresse de votre page dans l'en-tête Referer. Les URL complètes en révèlent plus qu'on ne le croit : termes de recherche, numéros de commande, le jeton d'un lien de réinitialisation de mot de passe.
strict-origin-when-cross-origin est la valeur raisonnable : l'URL complète au sein de votre site, seulement l'origine (https://example.com/) vers les autres sites, et rien quand une page HTTPS pointe vers du HTTP simple. Les navigateurs actuels appliquent déjà ce comportement, ou plus strict, quand la page ne dit rien ; le fixer explicitement le maintient quelle que soit la valeur par défaut d'un navigateur. Pour une application aux URL sensibles, no-referrer n'envoie rien du tout, au prix des données de provenance dans l'analytique des autres sites.
Permissions-Policy : coupez ce que vous n'utilisez pas
Permissions-Policy décide quelles fonctions puissantes du navigateur votre page et ses cadres peuvent utiliser : caméra, micro, géolocalisation, paiement, USB et d'autres. Une liste vide () coupe une fonction pour tous, (self) ne l'autorise qu'à votre propre origine, et vous pouvez nommer les origines des contenus intégrés qui en ont réellement besoin.
Un site d'entreprise ou une boutique n'en a presque jamais besoin, donc camera=(), microphone=(), geolocation=() est un bon départ ; ajoutez-en au fil de la revue de ce que vous intégrez. Le gain, c'est le confinement : un script tiers compromis ou un cadre publicitaire ne peut pas demander sa caméra au visiteur. Les navigateurs basés sur Chromium appliquent l'en-tête ; voyez-le comme un durcissement qui s'ajoute aux autres contrôles, pas comme un remplaçant.
Content-Security-Policy : puissant, et mérite un plan
Une Content Security Policy indique au navigateur d'où peuvent venir les scripts, les styles, les images, les polices, les connexions et les cadres, et si du code en ligne peut s'exécuter. Bien faite, elle transforme la plupart des failles de cross-site scripting : au lieu d'« un attaquant exécute du code dans la session de vos utilisateurs », on obtient « le navigateur a bloqué un script et l'a signalé ».
C'est aussi l'en-tête qui casse des pages. Blocs <script> en ligne, attributs onclick, un gestionnaire de balises qui injecte d'autres scripts, un point de collecte analytique oublié : chacun doit être autorisé ou réécrit. Déployez-la donc en deux étapes. Envoyez d'abord la politique en Content-Security-Policy-Report-Only : rien n'est bloqué, et chaque violation apparaît dans la console et, avec un point report-to ou report-uri, dans vos rapports. Corrigez ou autorisez ce que montrent les rapports, puis envoyez la même politique en Content-Security-Policy.
La politique ci-dessous est stricte mais lisible. Pour les scripts en ligne que vous ne pouvez pas retirer, générez un nonce aléatoire à chaque réponse et placez-le à la fois dans la politique et dans la balise <script>. Avec 'strict-dynamic', les scripts chargés par un script de confiance sont eux aussi de confiance, ce qui rend les gestionnaires de balises utilisables sans lister chaque domaine qu'ils touchent. Évitez 'unsafe-inline' pour les scripts : il désactive l'essentiel de la protection pour laquelle la politique existe.
Une politique de départ stricte, en mode report-only
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'; upgrade-insecure-requests
Des valeurs sûres à copier
La plupart des sites peuvent partir de cet ensemble et n'ajuster que les sources de la CSP et la liste des permissions. L'ordre n'a pas d'importance ; ce qui compte, c'est que chaque en-tête n'apparaisse qu'une fois.
X-XSS-Protection manque volontairement. Il pilotait un filtre que les navigateurs actuels ont retiré, et dans les anciens le filtre lui-même pouvait être détourné ; n'envoyez rien ou envoyez X-XSS-Protection: 0. Expect-CT est obsolète pour une raison du même ordre et peut disparaître aussi.
En-têtes de réponse pour une page HTML
Strict-Transport-Security: max-age=31536000
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Security-Policy-Report-Only: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
Les définir sur CDN.com.tr
HSTS a son propre préréglage, décrit plus haut. Les autres en-têtes à valeur unique vont dans une règle de diffusion : dans Règles de diffusion, modifiez la règle qui sert vos pages, en général /, et ajoutez-en un par ligne dans En-têtes personnalisés, sous la forme Nom-En-tête valeur (sur la page des règles à onglets : En-têtes → Ajouter des en-têtes de réponse). Une valeur qui contient des espaces se met entre guillemets doubles.
Une Content-Security-Policy complète ne peut pas y aller. Ses directives sont séparées par des points-virgules, et le panneau comme la périphérie refusent ; dans une ligne d'en-tête, parce qu'un point-virgule terminerait la propre directive de configuration de la périphérie. Une directive seule n'a pas de point-virgule, donc frame-ancestors seul fonctionne en périphérie. La politique complète a sa place sur votre serveur d'origine, dans le serveur web ou l'application, où elle peut aussi porter un nonce par requête. Une balise HTML <meta http-equiv="Content-Security-Policy"> fonctionne également, sauf pour frame-ancestors, report-uri et sandbox, que les navigateurs ignorent dans une balise meta.
Les en-têtes ajoutés par une règle s'appliquent aux réponses réussies et de redirection (2xx et 3xx), réponses servies depuis le cache comprises, dès la publication de la règle ; un en-tête qui doit aussi figurer sur les pages d'erreur relève du serveur d'origine. Les en-têtes envoyés par votre serveur d'origine sont stockés avec chaque copie en cache : après les avoir modifiés, purgez le cache du CDN pour les pages concernées. Si votre serveur d'origine envoie déjà l'un de ces en-têtes, sélectionnez-le dans Masquer les En-têtes dans la même règle, sinon les visiteurs en reçoivent deux exemplaires.
En-têtes personnalisés sur la règle qui sert vos pages
X-Content-Type-Options nosniff
X-Frame-Options SAMEORIGIN
Referrer-Policy strict-origin-when-cross-origin
Permissions-Policy "camera=(), microphone=(), geolocation=()"
Content-Security-Policy "frame-ancestors 'self'"
Comment les tester
Commencez par curl, qui montre exactement ce qu'envoie le serveur. Vérifiez une page, un fichier statique et une page qui n'existe pas : les en-têtes définis à un seul endroit manquent souvent aux deux autres.
Ouvrez ensuite les outils de développement du navigateur. L'onglet Network affiche les en-têtes tels que le navigateur les a reçus, et la console signale chaque violation de CSP et chaque cadre refusé, avec la directive en cause. Les scanners en ligne comme l'HTTP Observatory de Mozilla notent l'ensemble et expliquent chaque constat, ce qui fait un bon deuxième avis.
Enfin, testez le comportement et pas seulement la présence : intégrez la page dans une iframe sur une autre origine et vérifiez que le navigateur la refuse, puis surveillez la console quelques jours avec la politique report-only avant de l'appliquer.
curl -sI https://www.example.com/ | grep -i -E \
'strict-transport|x-content-type|x-frame|referrer-policy|permissions-policy|content-security'
# a static file and a missing page, too
curl -sI https://www.example.com/assets/app.css | grep -i x-content-type
curl -sI https://www.example.com/no-such-page | grep -i -E 'x-frame|content-security'
Questions fréquentes sur les en-têtes de sécurité
Quels en-têtes de sécurité tout site web devrait-il envoyer ?
HSTS, une fois que tout le site fonctionne en HTTPS ; X-Content-Type-Options: nosniff ; X-Frame-Options: SAMEORIGIN ou un frame-ancestors CSP ; Referrer-Policy: strict-origin-when-cross-origin ; et une Permissions-Policy qui coupe les fonctions inutilisées. Ajoutez une Content-Security-Policy après un essai en report-only.
X-Frame-Options est-il obsolète ?
Dépassé plutôt que supprimé. frame-ancestors de CSP fait le même travail avec plus de contrôle, et les navigateurs qui le prennent en charge ignorent X-Frame-Options quand les deux sont présents. Envoyer SAMEORIGIN ou DENY à côté protège toujours les anciens navigateurs ; seul ALLOW-FROM est mort.
Faut-il encore envoyer X-XSS-Protection ?
Non. Le filtre qu'il pilotait a été retiré des navigateurs actuels, et dans les anciens il pouvait être détourné contre la page. Ne l'envoyez pas ou envoyez 0, et comptez plutôt sur une Content-Security-Policy.
Puis-je définir Content-Security-Policy via CDN.com.tr ?
Une directive seule, oui : par exemple frame-ancestors 'self' dans les En-têtes personnalisés d'une règle de diffusion. Une politique complète exige des points-virgules entre ses directives, que les règles de périphérie n'acceptent pas ; définissez-la dans votre serveur web ou votre application, ou dans une balise meta pour tout sauf frame-ancestors, report-uri et sandbox.
Les en-têtes de sécurité ont-ils un effet sur le SEO ou la vitesse ?
Pas de façon mesurable. Ils ajoutent quelques centaines d'octets, et HTTP/2 comme HTTP/3 compressent les en-têtes répétés. Ce ne sont pas des facteurs de classement documentés. Le seul risque SEO est une CSP qui bloque vos propres scripts et casse l'affichage de la page, ce que l'étape report-only détecte.
Les images, le CSS et le JavaScript ont-ils aussi besoin de ces en-têtes ?
nosniff et HSTS sont utiles sur toutes les réponses. Les autres, CSP, protection contre l'encadrement, Referrer-Policy et Permissions-Policy, agissent sur les documents : ce sont les pages HTML qui comptent. Les envoyer partout ne fait aucun mal.