Connaître la différence entre les requêtes HTTP simple et XHR.
Application Web Origin - https://github.com
Lorsque vous ouvrez un site Web en tapant Origin Web App, votre navigateur effectue des requêtes HTTP simples. CORS n'est pas applicable ici. Comme nous demandons volontairement de diffuser du contenu depuis Origin Web App. Votre navigateur le servira/devrait le servir sans aucun problème. Mais, en ce qui concerne les requêtes XHR, JavaScript chargé par Origin Web App dans votre navigateur peut adresser une requête à n'importe quel serveur (https://netflix.com) demandant une ressource. Désormais, Origin Web App appartient à Github mais https://netflix.com appartient à Netflix, ce serveur pourrait potentiellement servir n'importe quoi. Github ne peut pas prendre le contrôle d'un serveur appartenant à Netflix. Cela a de nombreuses implications en matière de sécurité, en volant potentiellement le contenu d'un site Web (il peut s'agir d'un site Web financier) vers n'importe quel serveur distant.
Heureusement, CORS résout très bien ce problème avec un ensemble de règles donné.
Il s'agit d'une norme définie par le W3C pour permettre aux requêtes d'origine croisée entre le client (navigateur) et le serveur de partager des ressources tout en maintenant la sécurité. Tout navigateur se conformera à ces normes pour empêcher le chargement de ressources à partir de serveurs tiers.
Un en-tête indique d'où provient une demande. Il est envoyé avec les requêtes CORS, ainsi qu'avec les requêtes POST.
Syntaxe:
Origin: <scheme> "://" <hostname> [":" <port> ]
Exemples :
Origin: https://netflix.com
Origin: http://netflix.com:443
Origin: http://localhost:1443
Cet en-tête est utilisé par le navigateur lors de l'émission d'une requête préalable au vol pour indiquer quelle méthode de requête sera utilisée lorsque la requête réelle est effectuée.
Syntaxe:
Access-Control-Request-Method: <method>
<method>
peut être GET
, POST
ou DELETE
.
Exemple:
Access-Control-Request-Method: POST
Cet en-tête est à nouveau utilisé dans les requêtes préalables au vol par le navigateur pour indiquer quels en-têtes de requête doivent être utilisés lorsque la requête réelle est effectuée. Pour utiliser plusieurs en-têtes, ils doivent être séparés par une virgule.
Syntaxe:
Access-Control-Request-Headers: <header-name>
Access-Control-Request-Headers: <header-name>, <header-name>
Exemple:
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
Important : tous les en-têtes doivent être sur une liste sécurisée CORS ou un en-tête personnalisé tel que X-Custom-Header
.
Accept
Accept-Language
Content-Language
Content-Type
Les valeurs autorisées sont application/x-www-form-urlencoded
, multipart/form-data
et text/plain
.
Les en-têtes suivants sont renvoyés dans les demandes de pré-vol.
Cet en-tête indique que si l'origine demandée est autorisée. Votre navigateur choisira de réussir/échouer la demande en faisant correspondre l'origine demandée avec celle-ci.
Syntaxe:
Access-Control-Allow-Origin: *
Pour les demandes sans informations d'identification, la valeur *
peut être spécifiée comme caractère générique. Cela indique à votre navigateur d'autoriser les requêtes provenant de n'importe quelle origine.
Access-Control-Allow-Origin: <origin>
Lorsque vous ne recevez qu'une seule origine dans l'en-tête de réponse, cela signifie que votre serveur/application Web, en fonction de l' Origin
demandée, répond avec la même Origin
si cela est autorisé. Votre serveur doit également répondre avec Vary pour indiquer qu'il varie en fonction d'un en-tête de requête.
Exemple:
Access-Control-Allow-Origin: https://github.com
Votre navigateur fera une demande réelle si l'une des valeurs de cet en-tête correspond. Lorsque le caractère générique *
est renvoyé, cela signifie que n'importe quelle méthode est autorisée.
Syntaxe:
Access-Control-Allow-Methods: <method>, <method>, ...
Access-Control-Allow-Methods: *
Exemple:
Access-Control-Allow-Methods: GET, POST
Votre navigateur fera une demande réelle si tous les en-têtes demandés sont autorisés.
Syntaxe:
Access-Control-Allow-Headers: <header-name>, <header-name>
Access-Control-Allow-Headers: *
Exemple:
Access-Control-Allow-Headers: Accept, Content-Type
Wildcard *
indique au navigateur que tout en-tête est autorisé.
Indique combien de temps les résultats du pré-vol peuvent être mis en cache.
Access-Control-Max-Age: <delta-seconds>
nombre maximum. de secondes, les résultats peuvent être mis en cache.
Chaque navigateur a une limite maximale,
L'en-tête Vary est généralement utilisé à des fins de mise en cache pour déterminer si une réponse mise en cache peut être utilisée ou si elle doit être remise en cache en fonction de la valeur de l'en-tête.
Dans CORS, disons que le serveur autorise plusieurs origines en fonction de l' Origin
demandée, il renverra une URL particulière dans Access-Control-Allow-Origin
.
Syntaxe:
Vary: <header-name>
Exemple:
Disons que github.com permet à la fois à https://github.com et à https://netflix.com de demander des ressources. Envisagez les scénarios suivants,
Scénario 1 :
curl -X OPTIONS https://github.com/api/v1/gists/1
Origin: https://github.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Content-Type
Access-Control-Allow-Origin: https://github.com
Vary: Origin
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 600
Désormais, dans ce scénario, le navigateur mettra en cache les résultats de cette demande de pré-vol pendant 10 minutes.
Scénario 2 :
curl -X OPTIONS https://github.com/api/v1/gists/1
Origin: https://netflix.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Content-Type
Access-Control-Allow-Origin: https://netflix.com
Vary: Origin
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 300
Vous remarquerez maintenant que Access-Control-Allow-Origin
contient https://netflix.com, c'est un exemple de la façon dont la réponse varie en fonction d' Origin
donné. Il en va de même pour l'âge maximum de cette réponse qui est mise en cache pendant seulement 5 minutes contrairement au premier scénario.
Certaines demandes ne déclenchent pas de demande de pré-vol pour CORS. C'est ce que nous appelons des requêtes simples . Il doit remplir les conditions suivantes :
Une des méthodes autorisées :
Outre les en-têtes qui sont automatiquement générés par l'agent utilisateur (par exemple, Connection
ou User-Agent
), les seuls en-têtes autorisés à être définis manuellement sont les en-têtes de requête sur liste sécurisée CORS et les éléments suivants :
DPR
Downlink
Save-Data
Viewport-Width
Width
Aucun écouteur d'événement n'est enregistré sur un objet XMLHttpRequestUpload
utilisé dans la demande.
Aucun objet ReadableStream
n'est utilisé dans la requête.
Exemple:
Demande:
GET /api/v1/public-data/ HTTP/1.1
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
En-têtes de réponse :
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
Lorsqu'une requête renvoie *
dans l'en-tête Access-Control-Allow-Origin
. Cela signifie qu'une demande peut être faite depuis n'importe quel Host
. Dans ce cas, votre navigateur ne fera pas de demande de pré-vol.
Votre navigateur effectuera une demande préalable au vol pour déterminer si la demande réelle peut être envoyée en toute sécurité.
Exemple:
const xhr = new XMLHttpRequest ( ) ;
xhr . open ( 'POST' , 'https://bar.com/api/v1/post-here/' ) ;
xhr . setRequestHeader ( 'X-PINGOTHER' , 'pingpong' ) ;
xhr . setRequestHeader ( 'Content-Type' , 'application/json;charset=UTF-8' ) ;
xhr . onreadystatechange = handler ;
xhr . send ( JSON . stringify ( { "email" : "[email protected]" } ) ) ;
Demande de pré-vol :
OPTIONS /api/v1/post-here/
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: application/json
Access-Control-Allow-Origin: https://foo.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type, Accept
Demande réelle :
POST -d '{foo: bar}' /api/v1/post-here/
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
X-PINGOTHER: pingpong
Content-Type: application/json;charset=UTF-8
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: application/json
Access-Control-Allow-Origin: https://foo.com
Lorsqu'un en-tête non standard tel que X-PINGOTHER
est défini, votre navigateur ne saura pas s'il est sûr de faire la demande. Afin de garantir sa sécurité, votre navigateur effectue une requête OPTIONS avec Access-Control-Request-Headers
contenant X-PINGOTHER
et Content-Type
. Lors de la validation avec les en-têtes de réponse de la demande préalable au vol, votre navigateur effectue la demande réelle.
En général, lorsque vous effectuez une demande XHR, les cookies ne sont pas transmis avec la demande. Lorsqu'il est nécessaire de transmettre des cookies, vous devrez définir un indicateur sur l'objet XMLHttpRequest
.
const xhr = new XMLHttpRequest ( ) ;
const url = 'http://bar.com/api/v1/credentialed-content/' ;
function callOtherDomain ( ) {
if ( invocation ) {
xhr . open ( 'GET' , url , true ) ;
xhr . withCredentials = true ;
xhr . onreadystatechange = handler ;
xhr . send ( ) ;
}
}
Demande:
POST -d '{foo: bar}' /api/v1/post-here/
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
X-PINGOTHER: pingpong
Content-Type: application/json;charset=UTF-8
Cookie: _session=NyV14oKXiS6HHopaf9nT
Lorsqu'une requête est effectuée XMLHttpRequest
et que l'indicateur withCredentials
est défini, votre navigateur transmettra le Cookie
dans l'en-tête de la requête.
Réponse:
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: application/json
Access-Control-Allow-Origin: https://foo.com
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: _session=AjBSqxj4T7bSySNTWeEm; expires=Wed, 31-05-2020 00:00:00 GMT
Lorsque votre navigateur remarque que Access-Control-Allow-Credentials
est défini sur true. Il respectera l'en-tête Set-Cookie
et définira le cookie.
Important : le caractère générique "*" ne doit pas être défini dans Access-Control-Allow-Origin
comme mentionné dans la section Demandes d'identification et caractères génériques.
Lorsqu'une demande d'informations d'identification est effectuée en définissant l'indicateur withCredentials
, Access-Control-Expose-Headers
doit être défini par le serveur pour indiquer au navigateur quels en-têtes sont accessibles.
Dans le monde pré-cors, par défaut, les en-têtes de réponse ne sont pas accessibles par navigateur dans la requête CORS. Il est donc explicite que le navigateur recherchera cet en-tête afin de lire les en-têtes exposés. De cette façon, la spécification CORS garantit que les anciens navigateurs ne tombent pas en panne.
Ceci est renvoyé dans les demandes de pré-vol. Lorsque votre navigateur voit cela, il peut accéder à l'en-tête Set-Cookie
. Comme nous l'avons mentionné ci-dessus, dans les requêtes XHR normales, votre navigateur ne transmettra pas Cookie
dans l'en-tête de requête ni ne lira l'en-tête de réponse Set-Cookie
.
Syntaxe:
Access-Control-Allow-Credentials: true
Vous pouvez trouver un exemple dans la section Demande avec informations d'identification.
Lorsque nous disons demande authentifiée, cela signifie les cookies transmis dans la requête XHR ou définis via l'en-tête de réponse Set-Cookie
. Lorsqu'une demande XHR est effectuée avec l'indicateur withCredentials, vous espérez recevoir une réponse ainsi que les cookies à définir. Mais vous ne pouvez pas vous attendre à ce que Access-Control-Allow-Origin
soit « * », car cela signifierait que n'importe quel site Web peut utiliser ces cookies. Pour cette raison, votre navigateur échouera à la demande s'il voit « * » dans l'en-tête de réponse Access-Control-Allow-Origin
.
Lorsqu'une demande de pré-vol répond par 301/302, certains navigateurs peuvent ne pas le prendre en charge actuellement. Vous pourriez obtenir des erreurs telles que :
The request was redirected to 'https://example.com/foo', which is disallowed for cross-origin requests that require preflight
Request requires preflight, which is disallowed to follow cross-origin redirect
Remarque : Pour des solutions de contournement, vérifiez les demandes de contrôle en amont et les documents de redirection par Mozilla.
Le navigateur dispose de paramètres pour rejeter tous les cookies third-party
, lorsqu'un utilisateur l'autorise. Par exemple, si une demande est effectuée depuis https://foo.com
et que le serveur se trouve sur https://bar.com
, votre navigateur ne définira pas les cookies envoyés par https://bar.com
.