Les en-têtes HTTP indiquent au navigateur web la manière de se comporter suivant différentes situations. Certaines de ces situations peuvent introduire des faiblesses de sécurité. Cet article a pour objectif de présenter ces en-têtes, dits “de sécurité”, et de comprendre leur fonctionnement.
Les en-têtes de sécurité
Strict-Transport-Security (HSTS)
Afin de garantir la confidentialité et l’intégrité des échanges, chaque requête vers un site web doit être réalisée sur un flux chiffré en HTTPS. La bonne pratique consiste à rediriger systématiquement l’utilisateur de HTTP vers HTTPS (avec un code 301). Cela sous-entend que le navigateur commence par exécuter une première requête en HTTP avant d’être redirigé vers la version HTTPS du site. C’est la première requête qu’un attaquant va utiliser pour son attaque de type Man-In-The-Middle : il redirige l’utilisateur vers un autre site (sous son contrôle) ou encore le force à rester sur la version en clair du site.
L’en-tête HTTP Strict-Transport-Security
(HSTS) indique au navigateur qu’il doit
se rappeler d’aller sur le site directement en HTTPS lors des
prochaines visites. Il se présente de la forme suivante :
Strict-Transport-Security: max-age=<TEMPS>; <includeSubDomains;> <preload;>
max-age
indique le temps de conservation de l’information, il doit être positionné assez longtemps entre deux visites sur le site (par exemple 1 ans (31536000
) ou 2 ans (63072000
)), la valeur peut être augmenté progressivemen en vérifiant si des problèmes surviennentincludeSubDomains
indique si les sous-domaines (du domaine actuel) doivent aussi être incluspreload
indique que le navigateur peut partager cette information sur une base de données commune du navogateur (il ne fait pas partie de la spécification)
Pour le déploiement, il est recommandé d’augmenter progressivement
le max-age
en vérifiant si des problèmes surviennent.
X-Frame-Options
Une page web peut inclure une autre page à l’aide de la balise iframe
(frame
, embed
ou encore object
). Un utilisateur peut se faire piéger
par un site malveillant qui inclue le site victime dans une iframe et superpose
un autre contenu. L’utilisateur pense alors interagir avec le contenu d’un site
alors qu’au final il est en train d’interagir avec le site victime caché en dessous.
Le site victime peut interdire aux navigateurs de l’encapsuler dans un autre
en positionnant l’en-tête de sécurité X-Frame-Options
à DENY
(ou SAMEORIGIN
pour un site qui utiliserait une iframe pour son fonctionnement).
Cross-Origin-Resource-Sharing (CORS)
Le CORS
est un mécanisme de sécurité qui consiste à ajouter un ensemble
d’en-têtes de sécurité contrôlant le partage de ressources entre deux
“Origines” différentes. Je n’entre pas dans le détail de ce mécanisme,
j’y consacrerai un article dédié.
Parmi les en-têtes CORS
, Access-Control-Allow-Origin
doit impérativement être
présent pour le bon fonctionnement d’une API entre deux sous domaines (donc
origines) différents. L’utilisation de la valeur *
est à proscrire car elle
ouvre les appels API à tous les sites externes.
Content-Security-Policy (CSP)
Nous venons de voir avec CORS
qui peut accéder à nos ressources. L’en-tête CSP
contrôle l’origine des ressources utilisées sur notre site. Entre autres, cette
protection permet d’interdire l’exécution de scripts présents directement dans
le code HTML
de la page et donc de se prémunir contre une famille d’attaques
de type Cross Site Scripting
(XSS) les plus simples.
L’en-tête Content-Security-Policy
est composé d’une liste de directives
correspondant à chaque type de ressource (font
, img
, frame
, …). Chaque
directive déclare l’origine de la source des ressources. Le détail complet est
présent sur la documentation de Mozilla.
Afin de construire un ensemble de directives robuste, il convient d’interdire par
défaut l’ensemble des ressources (default-src 'none';
) puis d’autoriser une à
une les ressources (internes ou externes) à charger. Ce travail est plus facile
lorsqu’il est réalisé dès la création du site web.
Des nombreux exemples sont fournis sur Internet pour comprendre ce fonctionnement :
Pour aider au déploiement, l’en-tête Content-Security-Policy-Report-Only
permet d’expérimenter la CSP sans effectuer de blocage. Il ne faut pas oublier la directive
report-uri
pour recevoir un rapport lorsqu’une ressource contrevient aux règles
de la CSP.
X-Content-Type-Options
Lorsque le navigateur demande une ressource, le serveur retourne le fichier
demandé et, normalement, un en-tête Content-Type
qui indique la nature du fichier.
Lorsque l’en-tête n’est pas présent ou s’il ne correspond pas à ce qu’attend le
navigateur, alors il peut mettre en place un mécanisme appelé MIME sniffing : il identifie le type de fichier suivant l’extension dans l’URL ou encore les
premiers octets du fichiers (chaque navigateur a un heuristique propre).
Lorsque l’en-tête X-Content-Type-Options
a la valeur nosniff
, le mécanisme n’est pas utilisé.
Pourquoi cela pose-t-il un problème ? Un site peut autoriser le chargement d’un fichier
sur le serveur mais force ensuite un Content-Type
lors du téléchargement du fichier.
Un attaquant peut alors charger un certain fichier et réussir à l’ouvrir dans
le navigateur de sa victime. Par exemple, un fichier texte contenant du code HTML
est envoyé au site et l’URL de téléchargement est ensuite envoyée à la victime.
Le navigateur de la victime va ouvrir et interpréter le fichier HTML alors qu’il
aurait dû ne rien faire.
Referrer-Policy
L’en-tête Referer
est inclus automatiquement à chaque requête HTTP du
navigateur pour indiquer au serveur la page actuellement visitée.
Le serveur connait ainsi la page qui demande une ressource.
Un serveur tiers héberge une feuille de style CSS. Lorsqu’elle est demandée
(autorisée par les CSP
) par le site victime, le serveur tiers web reçoit l’URL
de la page. C’est ici qu’une fuite d’information peut avoir lieu. Si des secrets
sont inclus dans l’URL, comme par exemple le jeton de réinitialisation
du mot de passe, alors il sera envoyé à un site tiers. Ce dernier est alors en
mesure de renouveler le mot de passe de l’utilisateur avant lui et lui voler son compte.
L’en-tête Referrer-Policy
(avec deux r
) indique au navigateur comment
celui-ci doit se comporter avec l’en-tête Referer
(avec un seul r
).
Plusieurs valeurs sont possibles, il convient de choisir la valeur adéquate pour
limiter la fuite d’information sur des services tiers.
Referrer-Policy: no-referrer
Referrer-Policy: no-referrer-when-downgrade
Referrer-Policy: origin
Referrer-Policy: origin-when-cross-origin
Referrer-Policy: same-origin
Referrer-Policy: strict-origin
Referrer-Policy: strict-origin-when-cross-origin
Referrer-Policy: unsafe-url
La valeur unsafe-url
n’est pas recommandée car elle positionne l’ensemble de
l’URL (dont les paramètres) dans l’en-tête Referer
. Un effort est apporté par
les navigateurs pour ne plus inclure le chemin de l’URL dans l’en-tête. C’est le
cas de Firefox depuis le version 87.
Set-Cookie
Cela peut surprendre mais j’ajoute l’en-tête Set-Cookie
à cette liste parce
qu’il permet lui aussi d’apporter de la sécurité. Lorsqu’un cookie est ajouté,
plusieurs attributs sont à positionner :
Secure
: interdit l’envoi du cookie dans les requêtes non sécurisées HTTPHttpOnly
: interdit l’accès à la valeur du cookie depuis du code JavascriptSameSite
: contrôle l’envoi du cookie à certaines conditions
Pour un cookie de session (pas forcément pour un cookie applicatif),
les attributs Secure
et HttpOnly
doivent impérativement être utilisés.
L’attribut SameSite
est souvent moins connu, il fera l’objet d’un article dédié.
X-XSS-Protection
Quand l’en-tête X-XSS-Protection
est présent avec la valeur 1
, le navigateur
est invité à détecter les tentatives d’attaque de type Cross-Site-Scripting
.
Néanmoins, les chercheurs de vulnérabilités identifiée fréquement de nouvelles
manières de contourner les filtres utilisés par cette protection.
Comme cela a été évoqué plus haut, l’en-tête CSP
contrôle l’origine des scripts exécutés
sur la page. Avec une valeur correctement positionnée, les vulnérabilités XSS
les plus simples ne seront pas exécutées : l’attaquant devra aussi contourner
la protection CSP
.
Content-Security-Policy: script-src https://vimate.fr;
Forts de ce nouvel en-tête, les navigateurs modernes
ne supportent plus l’en-tête X-XSS-Protection
, il n’est donc plus recommandé
de l’envoyer. Il est même indiqué par l’OWASP de spécifier la valeur à 0 ;
le blocage du site devient un oracle permettant de déterminer si le site contient certaines informations.
D’autres en-têtes de sécurité
D’autres en-têtes existent pour contrôler la manière dont le navigateur doit se comporter avec les données du site. Ces en-têtes sont à positionner dans des cas particuliers :
- Clear-Site-Data
- Cross-Origin-Opener-Policy (COOP)
- Cross-Origin-Embedder-Policy
- Cross-Origin-Resource-Policy
- Cache-Control
- Feature-Policy
Un exemple d’exploitation
Je mets à disposition un script Python sur un gist ici qui lance un serveur web avec différents exemples d’en-têtes de sécurité.
Implémenter les en-têtes dans son application
Les en-têtes HTTP peuvent être ajoutés par différents composants dans une application. En outre, dans certaines architectures, il peut y avoir plusieurs équipements entre un utilisateur et le code final. Par exemple :
- La gateway exposée sur Internet ;
- La terminaison SSL/TLS ;
- Un Web Application Firewall (WAF) ;
- Un load-balancer ;
- Le framework utilisé ;
- Le code qui gère la requête.
Il peut être alors difficile de savoir où positionner les en-têtes. Je vous propose ci-dessous des pistes de réflexion pour trouver le meilleur emplacement :
Strict-Transport-Security
fait référence au HTTPS, il est recommandé de la positionner sur la terminaison SSL/TLS qui contrôle le chiffrement des flux avec l’utilisateurCross-Origin-Resource-Sharing
etContent-Security-Policy
sont deux en-têtes qui peuvent avoir des conséquences sur l’affichage du site, il est donc recommandé de les positionner au plus près du code pour les applications complexesX-Frame-Options
,X-Content-Type-Options
etReferrer-Policy
peuvent être positionnés sur les couches les plus proches de l’utilisateur- Pour
Set-Cookie
l’attributHttpOnly
peut être positionné par l’application mais l’attributSecure
sera placé par la terminaison SSL/TLS
Enfin, si un des en-têtes n’est pas présent, cela ne représente pas forcément une
vulnérabilité. Chaque valeur doit être décidée dans le contexte de l’application.
Par exemple, le CORS
n’est pas obligatoire pour les pages HTML statiques et
le CSP
n’est pas obligatoire pour les API. Un cookie qui ne porte pas la
session n’a pas forcément besoin d’avoir l’attribut httpOnly
.
Synthèse
Voici un tableau reprenant les différents en-têtes. Les valeurs recommandées sont à adapter au contexte de chaque application.
Nom en-tête | Valeur recommandée | Lien documentation Mozilla |
---|---|---|
Strict-Transport-Security |
max-age=31536000 |
HTTP/Headers/Strict-Transport-Security |
X-Frame-Options |
DENY |
HTTP/Headers/X-Frame-Options |
Cross-Origin-Resource-Sharing |
Access-Control-Origin : https://domaine et autre en-têtes |
HTTP/CORS |
Content-Security-Policy |
default-src 'none'; et autres directives |
HTTP/CSP |
X-Content-Type-Options |
nosniff |
Headers/X-Content-Type-Options |
Referrer-Policy |
no-referrer |
HTTP/Headers/Referrer-Policy |
Set-Cookie |
Secure , httpOnly et SameSite=strict |
HTTP/Headers/Set-Cookie |