Réseaux & Web · L2 · Section 6/7
CORS (approfondi)
Progression
#CORS (approfondi)
Le navigateur impose la politique de même origine par défaut. CORS (Cross-Origin Resource Sharing) définit comment un serveur peut autoriser des requêtes depuis une autre origine de manière fine.
Objectifs d'apprentissage
- Prédire si une requête déclenche un prévol OPTIONS, rien qu'en lisant méthode et en-têtes.
- Écrire les en-têtes de réponse exacts et diagnostiquer un blocage CORS dans la console.
- Ne jamais combiner
Access-Control-Allow-Origin: *et des credentials.
#Prévol (preflight) et simples requêtes
Les simples requêtes (GET/HEAD/POST) sans en-têtes personnalisés et avec des types sûrs (application/x-www-form-urlencoded, multipart/form-data, text/plain) n'envoient pas de prévol. Dès que vous utilisez des en-têtes non simples (dont Authorization et Content-Type: application/json) ou des méthodes différentes (PUT, PATCH, DELETE), le navigateur émet un OPTIONS de prévol pour vérifier les autorisations.
En-têtes clés côté serveur:
Access-Control-Allow-Origin: origine autorisée ou*(interdit avec credentials).Access-Control-Allow-Methods: méthodes permises.Access-Control-Allow-Headers: en-têtes personnalisés permises.Access-Control-Allow-Credentials: true: autorise cookies/Authorization; alorsAllow-Origindoit être une origine exacte.Access-Control-Max-Age: mise en cache du prévol par le navigateur.
#Trace exacte: prévol puis requête réelle
Requête initiale côté front (https://app.example.com), donc cross-origin vers https://api.example.com:
1fetch('https://api.example.com/resource', {2 method: 'PUT',3 credentials: 'include',4 headers: { 'Content-Type': 'application/json', 'X-Trace-Id': 'abc123' },5 body: JSON.stringify({ v: 2 }),6})Échange complet observé (DevTools, colonne Méthode; le prévol apparaît comme OPTIONS):
11) OPTIONS /resource HTTP/1.12 Host: api.example.com3 Origin: https://app.example.com4 Access-Control-Request-Method: PUT5 Access-Control-Request-Headers: content-type,x-trace-id6 72) HTTP/1.1 204 No Content8 Access-Control-Allow-Origin: https://app.example.com9 Access-Control-Allow-Methods: GET, POST, PUT, DELETE10 Access-Control-Allow-Headers: Content-Type, X-Trace-Id11 Access-Control-Allow-Credentials: true12 Access-Control-Max-Age: 60013 Vary: Origin14 Lecture de la trace: (1) le navigateur, pas le développeur, émet le OPTIONS et y liste la méthode et les en-têtes non simples qu'il prévoit d'envoyer; (2) le serveur autorise précisément ces valeurs; le Vary: Origin évite qu'un cache serve renvoie l'autorisation d'une autre origine; (3) seulement alors la vraie requête part, identique à ce qui avait été annoncé; (4) la réponse réelle doit aussi porter Allow-Origin, sinon le blocage a lieu au moment de la lecture.
Si l'étape 2 renvoie un simple 404 ou 405 (route non déclarée pour OPTIONS), la console affiche « Response to preflight request didn't pass access control check: It does not have HTTP ok status »: la requête réelle ne partira jamais.
#Bonnes pratiques
Une politique CORS précise réduit la surface: répondez avec l'origine exacte attendue et limitez méthodes et en-têtes autorisés; variez la réponse sur Origin (Vary: Origin). Évitez les credentials pour des API publiques; si vous les utilisez, n'employez jamais * pour Allow-Origin. Pour des échanges inter-origines embarqués (iframes), préférez un protocole postMessage bien borné plutôt qu'une politique CORS large.
#Explorateur CORS interactif
Origin: https://app.example.com Access-Control-Request-Method: POST Access-Control-Request-Headers: Content-Type, X-Auth-Token Credentials: include
Access-Control-Allow-Methods: OPTIONS, GET, POST, PUT, DELETE Access-Control-Allow-Headers: Content-Type, X-Auth-Token Vary: Origin Access-Control-Allow-Origin: https://app.example.com Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://app.example.com Vary: Origin Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: X-Request-ID
#Flow: décision CORS
Sur une page servie depuis https://app.example.com, déterminez sans exécuter si chaque appel ci-dessous déclenche un prévol, puis vérifiez dans l'explorateur ci-dessus:
fetch('https://cdn.example.com/logo.png'). Correction: non. GET, aucun en-tête personnalisé: requête simple, le serveur n'a qu'à renvoyerAccess-Control-Allow-Origin.fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' } }). Correction: oui.application/jsonn'est pas un type sûr pour un POST; prévolOPTIONSavecAccess-Control-Request-Headers: content-type.fetch(url, { method: 'DELETE', credentials: 'include' }). Correction: oui. DELETE n'est jamais une méthode simple; et côté serveurAllow-Credentials: trueexige une origine exacte.fetch(url, { headers: { 'X-Api-Key': 'k' } }). Correction: oui.X-Api-Keyest un en-tête personnalisé, le prévol le liste dansAccess-Control-Request-Headers.
#Diagnostic: trois blocages typiques et leur signature console
- Absence d'
Allow-Originsur la réponse réelle: preflight réussi puis blocage à la lecture; console « No 'Access-Control-Allow-Origin' header is present ». Le prévol ne suffit pas, chaque réponse doit porter l'en-tête. - Origine non listée par le serveur: réponse 200 saine, lecture refusée; typique d'un serveur qui énumère des origines autorisées et oublie le port (
https://app.example.com:3000ethttps://app.example.comsont deux origines distinctes). - Erreur réseau pendant la requête CORS: la console mentionne un « réseau » et parfois CORS; vérifiez d'abord le serveur (
curl), CORS ne fait que masquer le détail d'une panne sous-jacente.
#Atelier
Écrivez deux réponses: (1) API publique sans credentials avec Allow-Origin: https://app.exemple, (2) API privée avec cookies (Allow-Origin: https://admin.exemple, Allow-Credentials: true, Vary: Origin). Comparez le comportement du navigateur avec et sans Max-Age sur le prévol pour observer l'impact sur la latence perçue. Observation attendue: sans Access-Control-Max-Age, chaque navigation émet un OPTIONS visible dans l'onglet Réseau; avec Max-Age: 600, plus aucun OPTIONS pendant dix minutes.
#Diagramme: prévol puis requête
Mini-exercice: listez les en-têtes de votre requête front (méthode, headers). Déterminez si elle est « simple ». Si non, proposez une politique CORS minimale côté serveur (origines, méthodes, en-têtes, Max-Age) et vérifiez chaque valeur annoncée par le prévol (Access-Control-Request-*) contre la politique que vous avez écrite.