J'ai un processus Grunt qui démarre une instance du serveur express.js. Tout cela fonctionnait parfaitement jusqu'à présent, mais maintenant, il commence à afficher une page blanche et affiche ce qui suit dans le journal des erreurs de la console développeur de Chrome (dernière version) :
XMLHttpRequest ne peut pas charger https://www.example.com/ Il n’y a pas d’en-tête « Access-Control-Allow-Origin » sur la ressource demandée. Par conséquent, l'accès depuis « http://localhost:4300 » n'est pas autorisé.
Qu'est-ce qui m'empêche d'accéder à cette page ?
Le serveur cible doit autoriser les requêtes inter-domaines. Afin de l'autoriser via express, il suffit de gérer la demande d'option http :
tl;dr — Lorsque vous souhaitez lire des données, (principalement) à l'aide de JS côté client, à partir d'un autre serveur, vous avez besoin que le serveur contenant les données accorde une autorisation explicite au code qui souhaite les données.
Il y a un résumé à la fin et des titres dans la réponse pour faciliter la recherche des parties pertinentes. Il est cependant recommandé de tout lire, car cela fournit des informations utiles pour comprendre le pourquoi, ce qui permet de voir plus facilement comment le comment s'applique dans différentes circonstances.
À propos de la politique de même origine
Il s'agit de la Politique de même origine. Il s'agit d'une fonctionnalité de sécurité mise en œuvre par les navigateurs.
Votre cas particulier montre comment il est implémenté pour XMLHttpRequest (et vous obtiendrez des résultats identiques si vous utilisiez fetch), mais il s'applique également à d'autres choses (comme les images chargées sur un
<canvas>
or documents loaded into an<iframe>
), juste avec des implémentations légèrement différentes .Le scénario standard qui démontre la nécessité de la SOP peut être démontré avec trois caractères :
https://www.example.com/
dans votre exemple)http://localhost:4300
dans votre exemple)Alice est connectée au site de Bob et y possède des données confidentielles. Il s'agit peut-être de l'intranet de l'entreprise (accessible uniquement aux navigateurs du réseau local) ou de sa banque en ligne (accessible uniquement avec un cookie que vous obtenez après avoir saisi un nom d'utilisateur et un mot de passe).
Alice visite le site Web de Mallory qui contient du JavaScript qui amène le navigateur d'Alice à envoyer une requête HTTP au site Web de Bob (à partir de son adresse IP avec ses cookies, etc.). Cela pourrait être aussi simple que d'utiliser
XMLHttpRequest
and reading theresponseText
.La politique de même origine du navigateur empêche JavaScript de lire les données renvoyées par le site Web de Bob (auquel Bob et Alice ne veulent pas que Mallory accède). (Notez que vous pouvez, par exemple, afficher une image en utilisant un élément
<img>
à travers les origines car le contenu de l'image n'est pas exposé à JavaScript (ou Mallory)… à moins que vous ne jetiez un canevas dans le mélange, auquel cas vous générez un erreur de violation de même origine).Pourquoi la politique de même origine s'applique alors que vous pensez qu'elle ne devrait pas le faire
Pour une URL donnée, il est possible que la SOP ne soit pas nécessaire. Voici quelques scénarios courants dans lesquels tel est le cas :
… mais le navigateur n'a aucun moyen de savoir si l'une des conditions ci-dessus est vraie, donc la confiance n'est pas automatique et la SOP est appliquée. L'autorisation doit être accordée explicitement avant que le navigateur ne transmette les données qu'il a reçues de Bob à un autre site Web.
Pourquoi la politique de même origine s'applique au JavaScript dans une page Web mais rien d'autre
En dehors de la page Web
LesExtensions de navigateur
*
, l'onglet Réseau dans les outils de développement du navigateur et les applications comme Postman sont des logiciels installés. Ils ne transmettent pas de données d'un site Web au JavaScript appartenant à un autre site Web simplement parce que vous avez visité cet autre site Web. L'installation d'un logiciel nécessite généralement un choix plus conscient.Il n'y a pas de tiers (Mallory) qui soit considéré comme un risque.
*
Les extensions de navigateur doivent être écrites avec soin pour éviter les problèmes d'origine croisée. Voir la documentation Chrome par exemple.Dans la page Web
La plupart du temps, il n'y a pas beaucoup de fuite d'informations lorsqu'il suffit d'afficher quelque chose sur une page Web.
Si vous utilisez un attribut
<img>
element to load an image, then it gets shown on the page, but very little information is exposed to Mallory. JavaScript can't read the image (unless you use acrossOrigin
pour activer explicitement l'autorisation de demande avec CORS), puis copiez-le sur son serveur.Cela dit, certaines informations fuient, pour citer Domenic Denicola (de Google) :
C'est pourquoi vous avez besoin de l'autorisation CORS pour charger des polices sur plusieurs origines.
Pourquoi pouvez-vous afficher des données sur la page sans les lire avec JS
Il existe un certain nombre de circonstances dans lesquelles le site de Mallory peut amener un navigateur à récupérer des données auprès d'un tiers et à les afficher (par exemple en ajoutant un élément
<img>
pour afficher une image). Il n'est cependant pas possible pour le JavaScript de Mallory de lire les données de cette ressource, seuls le navigateur d'Alice et le serveur de Bob peuvent le faire, le système est donc toujours sécurisé.CORS
L'en-tête
Access-Control-Allow-Origin
HTTP response mentionné dans le message d'erreur fait partie de la norme CORS qui permet à Bob d'accorder explicitement l'autorisation au site de Mallory d'accéder aux données via le navigateur d'Alice.Une implémentation de base comprendrait simplement :
… dans les en-têtes de réponse pour permettre à tout site Web de lire les données.
… permettrait uniquement à un site spécifique d'y accéder, et Bob peut générer cela dynamiquement en fonction de l'en-tête
Origin
request pour permettre à plusieurs sites, mais pas à tous, d'y accéder.Les spécificités de la façon dont Bob définit cet en-tête de réponse dépendent du serveur HTTP de Bob et/ou du langage de programmation côté serveur. Les utilisateurs de Node.js/Express.js doivent utiliser le middleware CORS bien documenté. Les utilisateurs d'autres plates-formes devraient jeter un œil à cette collection de guides pour diverses configurations courantes qui pourraient aider.
NB : Certaines requêtes sont complexes et envoient une requête preflight OPTIONS à laquelle le serveur devra répondre avant que le navigateur n'envoie la requête GET/POST/PUT/Whatever que le JS souhaite faire. Les implémentations de CORS qui ajoutent uniquement
Access-Control-Allow-Origin
à des URL spécifiques sont souvent perturbées par cela.Évidemment, accorder une autorisation via CORS est quelque chose que Bob ne ferait que si :
Comment ajouter ces en-têtes ?
Cela dépend de votre environnement côté serveur.
Si vous le pouvez, utilisez une bibliothèque conçue pour gérer CORS car elle vous présentera des options simples au lieu d'avoir à tout gérer manuellement.
Enable-Cors.org propose une liste de documentation pour des plates-formes et des frameworks spécifiques qui pourraient vous être utiles.
Mais je ne suis pas Bob !
Il n'existe pas de mécanisme standard permettant à Mallory d'ajouter cet en-tête car il doit provenir du site Web de Bob, qu'elle ne contrôle pas.
Si Bob exécute une API publique, il peut y avoir un mécanisme pour activer CORS (peut-être en formatant la requête d'une certaine manière, ou une option de configuration après s'être connecté à un site du portail de développeur pour le site de Bob). Cela devra cependant être un mécanisme mis en œuvre par Bob. Mallory pourrait lire la documentation sur le site de Bob pour voir si quelque chose est disponible, ou elle pourrait parler à Bob et lui demander d'implémenter CORS.
Messages d'erreur mentionnant "Réponse pour le contrôle en amont"
Certaines demandes d'origine croisée font l'objet d'un contrôle en amont.
Cela se produit lorsque (en gros) vous essayez de faire une demande d'origine croisée qui :
enctype
ou dans un ou plusieurs autres en-têtes de requête).Si vous faites correctement quelque chose qui nécessite un contrôle en amont
Dans ces cas, le reste de cette réponse s'applique toujours mais vous devez également vous assurer que le serveur peut écouter la demande de contrôle en amont (qui sera
OPTIONS
(and notGET
,POST
, or whatever you were trying to send) and respond to it with the rightAccess-Control-Allow-Origin
header but alsoAccess-Control-Allow-Methods
andAccess-Control-Allow-Headers
pour autoriser vos méthodes ou en-têtes HTTP spécifiques.Si vous déclenchez un contrôle en amont par erreur
Parfois, les gens font des erreurs en essayant de construire des requêtes Ajax, et parfois cela déclenche la nécessité d'un contrôle en amont. Si l'API est conçue pour autoriser les requêtes d'origine croisée mais ne nécessite rien qui nécessiterait un contrôle en amont, cela peut interrompre l'accès.
Les erreurs courantes qui déclenchent cela incluent :
Access-Control-Allow-Origin
et d'autres en-têtes de réponse CORS sur la demande. Ceux-ci n'appartiennent pas à la demande, ne font rien d'utile (à quoi servirait un système d'autorisations où vous pourriez vous accorder la permission ?) et doivent apparaître uniquement sur la réponse.Content-Type: application/json
header on a GET request that has no request body the content of which to describe (typically when the author confusesContent-Type
andAccept
).Dans l'un ou l'autre de ces cas, la suppression de l'en-tête de requête supplémentaire sera souvent suffisante pour éviter le besoin d'un contrôle en amont (ce qui résoudra le problème lors de la communication avec des API prenant en charge les requêtes simples mais pas les requêtes pré-volées).
Réponses opaques (mode
no-cors
)Parfois, vous devez faire une requête HTTP, mais vous n'avez pas besoin de lire la réponse. par exemple. si vous publiez un message de journal sur le serveur pour enregistrement.
Si vous utilisez le
fetch
API (rather thanXMLHttpRequest
), vous pouvez le configurer pour ne pas essayer d'utiliser CORS.Notez que cela ne vous permettra pas de faire tout ce que vous demandez à CORS de faire. Vous ne pourrez pas lire la réponse. Vous ne pourrez pas faire de demande nécessitant un contrôle en amont.
Cela vous permettra de faire une simple demande, de ne pas voir la réponse et de ne pas remplir la console de développement de messages d'erreur.
Comment procéder est expliqué par le message d'erreur Chrome donné lorsque vous faites une demande en utilisant
fetch
et que vous n'obtenez pas l'autorisation d'afficher la réponse avec CORS :Ainsi :
Alternatives au CORS
JSONP
Bob pourrait également fournir les données à l'aide d'un hack comme JSONP, c'est ainsi que les gens utilisaient Ajax d'origine croisée avant l'arrivée de CORS.
Cela fonctionne en présentant les données sous la forme d'un programme JavaScript qui injecte les données dans la page de Mallory.
Cela nécessite que Mallory fasse confiance à Bob pour ne pas fournir de code malveillant.
Notez le thème commun : le site fournissant les données doit indiquer au navigateur qu'il est acceptable qu'un site tiers accède aux données qu'il envoie au navigateur.
Étant donné que JSONP fonctionne en ajoutant un élément
<script>
pour charger les données sous la forme d'un programme JavaScript qui appelle une fonction déjà dans la page, tenter d'utiliser la technique JSONP sur une URL qui renvoie JSON échouera - généralement avec une erreur CORB - parce que JSON n'est pas JavaScript.Déplacez les deux ressources vers une seule Origine
Si le document HTML dans lequel le JS s'exécute et l'URL demandée se trouvent sur la même origine (partageant le même schéma, le même nom d'hôte et le même port), la politique de même origine accorde l'autorisation par défaut. CORS n'est pas nécessaire.
Un proxy
Mallory pourrait utiliser du code côté serveur pour récupérer les données (qu'elle pourrait ensuite transmettre de son serveur au navigateur d'Alice via HTTP comme d'habitude).
Ce sera soit :
Ce code côté serveur peut être écrit et hébergé par un tiers (tel que CORS Anywhere). Notez les implications de ceci en matière de confidentialité : le tiers peut surveiller qui proxy quoi sur ses serveurs.
Bob n'aurait pas besoin d'accorder d'autorisations pour que cela se produise.
Il n'y a aucune implication en matière de sécurité ici puisque cela ne concerne que Mallory et Bob. Il n'y a aucun moyen pour Bob de penser que Mallory est Alice et de fournir à Mallory des données qui devraient rester confidentielles entre Alice et Bob.
Par conséquent, Mallory ne peut utiliser cette technique que pour lire des données publiques.
Notez cependant que prendre du contenu sur le site Web de quelqu'un d'autre et l'afficher par vous-même peut constituer une violation du droit d'auteur et vous exposer à des poursuites judiciaires.
Écrire autre chose qu'une application web
Comme indiqué dans la section « Pourquoi la politique de même origine s'applique uniquement au JavaScript dans une page Web », vous pouvez éviter le SOP en n'écrivant pas de JavaScript dans une page Web.
Cela ne signifie pas que vous ne pouvez pas continuer à utiliser JavaScript et HTML, mais vous pouvez les distribuer en utilisant un autre mécanisme, tel que Node-WebKit ou PhoneGap.
Extensions de navigateur
Il est possible qu'une extension de navigateur injecte les en-têtes CORS dans la réponse avant que la politique de même origine ne soit appliquée.
Ceux-ci peuvent être utiles pour le développement mais ne sont pas pratiques pour un site de production (demander à chaque utilisateur de votre site d'installer une extension de navigateur qui désactive une fonctionnalité de sécurité de son navigateur est déraisonnable).
Ils ont également tendance à fonctionner uniquement avec des requêtes simples (échouant lors du traitement des requêtes OPTIONS en amont).
Avoir un environnement de développement approprié avec un serveur de développement local est généralement une meilleure approche.
Autres risques de sécurité
Notez que SOP / CORS n'atténuent pas les attaques XSS, CSRF ou SQL Injection qui doivent être gérées indépendamment.
Résumé