Métadonnées de récupération
Une métadonnée de récupération est le terme désignant un groupe d'en-têtes de requête HTTP qui fournissent au serveur des informations sur le contexte dans lequel la requête est effectuée.
Entre autres choses, les métadonnées de récupération permettent au serveur de savoir :
-
Si la requête représente une navigation entre documents, une requête pour une sous-ressource, ou si elle a été effectuée explicitement depuis JavaScript, par exemple en utilisant l'API
fetch(). -
La relation entre le demandeur de la ressource et la ressource demandée : s'ils sont de la même origine, du même site, ou de sites complètement différents.
En utilisant les informations contenues dans ces en-têtes pour autoriser ou refuser des requêtes spécifiques, un serveur peut mettre en place une défense contre les attaques inter-origines telles que les falsifications de requêtes inter-sites (CSRF) et diverses fuites inter-sites.
Les en-têtes de métadonnées de récupération
La spécification des métadonnées de récupération (angl.) définit quatre en-têtes de métadonnées de récupération :
Comme tous les en-têtes préfixés par Sec-, ceux-ci sont des en-têtes de requête interdits, ce qui signifie qu'ils ne peuvent pas être définis ou modifiés par le code frontal du site web. De plus, les en-têtes de métadonnées de récupération ne sont envoyés que dans les requêtes vers des URL potentiellement fiables, de sorte que les serveurs sur des origines non sécurisées (http://) ne reçoivent pas ces en-têtes.
Sec-Fetch-Dest
Cet en-tête indique la destination de la requête. Cet attribut est défini dans l'API Fetch, où il est exposé en tant que propriété Request.destination.
On peut le considérer, de manière approximative, comme la manière dont la ressource retournée est utilisée.
Pour la plupart des éléments remplacés, la valeur de l'en-tête indique l'élément pour lequel cette ressource est utilisée, comme iframe, object, audio ou video. Une valeur de image indique que la ressource est utilisée comme image référencée par un élément remplacé tel qu'un élément HTML <img>, une propriété CSS background-image, un élément SVG <image>, ou tout autre endroit de la plateforme web qui utilise des images chargées à partir de sous-ressources.
D'autres valeurs de destination intéressantes incluent :
document-
La requête concerne un nouveau document qui est la cible d'une navigation de niveau supérieur (par exemple, l'utilisateur·ice cliquant sur un lien dans la page ou envoyant un formulaire).
script-
La ressource est utilisée comme script chargé à partir d'un élément HTML
<script>ou d'un appel àimportScripts()dans un web worker.Des valeurs plus spécifiques sont utilisées pour indiquer d'autres endroits où la ressource est utilisée comme script, tels que les worklets (
audioworkletetpaintworklet) et les workers (sharedworker,serviceworkeretworker). empty-
La requête n'a pas de destination définie : entre autres causes possibles, c'est la valeur donnée si la requête est le résultat d'un appel à
fetch().
Pour l'ensemble complet des valeurs possibles, voir la page de référence pour cet en-tête.
Sec-Fetch-Mode
Cet en-tête indique le mode de la requête. Comme pour la destination, le mode est défini dans l'API Fetch, où il est exposé en tant que propriété Request.mode.
Les valeurs les plus couramment utilisées sont :
-
La requête représente une navigation entre documents (par exemple, l'utilisateur·ice cliquant sur un lien).
no-cors-
La requête est effectuée en mode
no-cors.Cela signifie que la requête est autorisée inter-origine sans que le serveur n'envoie les en-têtes CORS appropriés, avec la restriction que la réponse ne peut pas être accessible par le JavaScript s'exécutant côté client (elle est opaque).
Il s'agit du mode par défaut pour les pages qui chargent des sous-ressources telles que des images, des polices, des scripts et des feuilles de style, et explique pourquoi un site différent est par défaut autorisé à utiliser les sous-ressources de votre site, même si vous n'avez pas configuré CORS pour le permettre.
cors-
Si la requête est inter-origine, le serveur doit répondre avec les en-têtes CORS appropriés, sinon la requête échoue. Si le serveur répond avec les en-têtes CORS appropriés, alors le corps de la réponse et certains en-têtes sont mis à disposition de l'appelant.
Cela se rencontre le plus souvent pour les requêtes inter-origine effectuées depuis JavaScript en utilisant l'API Fetch, lorsque l'appelant a besoin d'accéder à la ressource retournée (par exemple, un appel fetch pour récupérer du JSON depuis le serveur).
same-origin-
La requête n'est autorisée que si l'appelant est de la même origine que la ressource demandée.
Sec-Fetch-Site
Ce en-tête indique la relation entre l'origine de la ressource demandée et l'origine du demandeur de la ressource.
Il indique si le demandeur provient de :
- La même origine que la ressource demandée.
- Une origine différente, mais le même site.
- Un site différent.
Par exemple, si un·e utilisateur·ice clique sur un lien dans une page à https://books.example.org/authors, le navigateur effectue une requête pour récupérer le document défini dans la cible du lien. Le tableau suivant montre les valeurs de l'en-tête Sec-Fetch-Site associé pour différentes valeurs de cible de lien :
| Cible du lien | Valeur de Sec-Fetch-Site |
|---|---|
https://books.example.org/titles |
same-origin |
https://login.example.org/ |
same-site |
https://books.example.com/titles |
cross-site |
Des correspondances similaires s'appliquent à d'autres requêtes HTTP, telles que :
- Les envois de formulaire par l'attribut
actiond'un élément HTML<form>. - Les requêtes pour des sous-ressources telles que des images, des polices ou des scripts.
- Les requêtes effectuées à l'aide de l'API
fetch().
L'en-tête Sec-Fetch-Site peut également avoir la valeur none pour les requêtes qui n'ont pas de site en tant que demandeur, y compris, par exemple, les requêtes effectuées lorsque l'utilisateur·ice saisit une URL dans la barre d'adresse du navigateur ou clique sur un favori. La spécification appelle ces requêtes directement initiées par l'utilisateur·ice (angl.).
Sec-Fetch-User
Cet en-tête n'est inclus que si la requête a été initiée par une action de l'utilisateur·ice (comme un clic sur un lien), et s'il est inclus, il a toujours la valeur ?1.
Attaques inter-origines
Une métadonnée de récupération est particulièrement utile comme défense contre les attaques inter-origines. Ces attaques ciblent généralement un·e utilisateur·ice qui a un compte sur un site légitime et qui est connecté·e à ce site. L'attaquant·e crée un site web qui effectue une requête inter-origines vers le site légitime, puis trompe l'utilisateur·ice pour qu'il·elle exécute cette requête.
Note : Nous utilisons le terme attaque inter-origines dans ce guide, bien que de nombreuses attaques soient conventionnellement appelées attaques inter-sites.
Une origine est un concept plus restrictif qu'un site. En particulier, un site inclut les sous-domaines d'un domaine, et une origine ne le fait pas : ainsi https://example.org et https://login.example.org sont le même site, mais des origines différentes.
Cela signifie que, bien que toutes les attaques inter-sites soient des attaques inter-origines, certaines attaques inter-origines ne sont pas des attaques inter-sites. Par exemple, si un·e attaquant·e prend le contrôle d'un sous-domaine d'un site, il·elle peut alors attaquer le site en utilisant des requêtes inter-origines et dans le site. Pour inclure ces attaques, nous utilisons le terme plus restrictif.
Par exemple, le site de l'attaquant·e peut contenir un élément HTML <form> qui envoie au site légitime. Pour certaines attaques inter-origines, aucune interaction de l'utilisateur·ice n'est nécessaire : la page de l'attaquant·e peut simplement exécuter une requête fetch() vers le site légitime lors du chargement de la page, et l'utilisateur·ice n'a alors qu'à ouvrir la page de l'attaquant·e pour que la requête inter-origines soit exécutée.
Parce que la requête provient du navigateur de l'utilisateur·ice, elle inclut tous les cookies définis pour l'utilisateur·ice par le site légitime, y compris les cookies que le site légitime utilise pour identifier les utilisateur·ice·s. La requête se voit donc attribuer les privilèges de cet utilisateur·ice.
On peut distinguer deux types d'attaques inter-origines :
-
Falsification de requête inter-sites (CSRF) : dans ces attaques, la requête inter-origines effectue une action conséquente sur le serveur légitime, en utilisant des paramètres fournis par l'attaquant·e. Par exemple, la requête demande au serveur de transférer de l'argent du compte de l'utilisateur·ice ciblé·e vers le compte de l'attaquant·e.
-
Fuites inter-sites : dans ces attaques, l'attaquant·e utilise la requête pour obtenir des informations sur la relation de l'utilisateur·ice avec le site cible, souvent par le biais de canaux secondaires tels que les évènements d'erreur.
La plupart des sites Web veulent refuser certaines requêtes inter-origines tout en en autorisant d'autres : par exemple, si vous refusez toutes les requêtes inter-origines, personne ne peut naviguer vers votre site depuis un site différent !
En utilisant les métadonnées de récupération, un serveur peut construire une politique pour autoriser ou refuser les requêtes inter-origines en fonction des détails de leur contexte.
Politique d'isolation des ressources
Un type de politique courant est appelé politique d'isolation des ressources. Lorsque le serveur reçoit une requête, il examine les en-têtes de métadonnées de récupération de la requête pour n'autoriser que :
- Les requêtes de même origine (et parfois les requêtes du même site, si vous faites confiance à vos sous-domaines).
- Les requêtes de navigation de premier niveau provenant d'une autre origine, afin que les utilisateur·ice·s puissent accéder à votre site en cliquant sur des liens dans d'autres sites.
- Les requêtes vers des points de terminaison spécifiques qui sont destinés à être accessibles inter-origines, y compris ceux qui utilisent CORS.
Par exemple, le code Express suivant n'autorise que les requêtes de même origine, les requêtes directement initiées par l'utilisateur·ice et les navigations.
function estAutorise(req) {
// Permet les requêtes de même origine
// Permet les requêtes directement initiées par l'utilisateur·ice (à partir des favoris, de la barre d'adresse, etc.)
const secFetchSite = req.headers["sec-fetch-site"];
if (secFetchSite === "same-origin" || secFetchSite === "none") {
return true;
}
// Permet les navigations inter-sites, comme cliquer sur des liens
const secFetchMode = req.headers["sec-fetch-mode"];
if (secFetchMode === "navigate" && req.method === "GET") {
return true;
}
// Refuse tout le reste
return false;
}
app.get("/admin", (req, res) => {
res.setHeader("Vary", "sec-fetch-site, sec-fetch-mode");
if (estAutorise(req)) {
// Répond avec la page d'administration si l'utilisateur·ice est admin
getAdminPage(req, res);
} else {
res.status(403).send("Interdit");
}
});
Notez qu'il envoie également l'en-tête de réponse Vary. Cela garantit que si la réponse est mise en cache, la réponse mise en cache n'est donnée qu'aux requêtes ayant les mêmes valeurs pour les en-têtes de métadonnées de récupération que nous utilisons.
La page Politique d'isolation des ressources (angl.) fournit plus d'exemples de code pour une politique d'isolation des ressources.