CORP vs COEP, deux faces de la même isolation cross-origin
CentralCSP Team ·
Dernière mise à jour:
Ces deux headers se ressemblent et on les confond sans arrêt, mais ils se tiennent aux deux extrémités opposées de la même requête. Cross-Origin-Resource-Policy (CORP) est quelque chose qu'une ressource envoie à propos d'elle-même. Cross-Origin-Embedder-Policy (COEP) est quelque chose que la page qui embarque envoie à propos de ce qu'elle acceptera. Une fois que vous savez de quel côté vous êtes, la confusion se dissipe.
En bref : CORP est un header de réponse qu'une ressource pose pour déclarer qui peut la charger. COEP est un header que la page qui embarque pose pour déclarer qu'elle ne charge que des ressources qui ont accepté. Ce sont deux moitiés du même mécanisme, pas des concurrents, et ils sont conçus pour être utilisés ensemble.
CORP vs COEP, la différence en une ligne
CORP est posé par la ressource et répond à « qui peut me charger ». COEP est posé par la page et dit « je ne charge que des ressources qui ont accepté ».
Tout le reste en découle. Un CDN, un hébergeur d'images, un serveur de polices, chacun d'eux est une ressource, donc c'est à lui d'envoyer CORP. La page qui tire ces ressources est l'embarqueur, donc c'est à l'embarqueur d'envoyer COEP. La ressource déclare sa propre accessibilité ; la page déclare sa propre règle d'admission.
Cross-Origin-Resource-Policy, posé par la ressource
CORP est un header de réponse que le navigateur lit quand une autre origine tente de
charger votre ressource via une requête no-cors (un simple <img>, <script>,
<audio>, ou une feuille de style sans crossorigin). Il a d'abord été proposé en
2012 sous la forme d'un header nommé From-Origin, puis relancé en 2018 après que
Spectre a fait des données cross-origin dans le mauvais processus une fuite bien
réelle. Il a exactement trois valeurs.
Cross-Origin-Resource-Policy: same-originsame-origin: seule l'origine exactement identique (schéma, hôte et port) obtient la ressource. Le choix deny-par-défaut.same-site: le même domaine enregistrable, donc les sous-domaines sont autorisés. C'est ce dont a besoin uncdn.example.comqui sertwww.example.com;same-originle casserait. Une réserve :same-sitesur une ressource HTTPS n'admet pas une page en HTTP simple du même site, donc une configuration à schémas mixtes échoue quand même au contrôle.cross-origin: n'importe quelle origine peut la charger. Cela existe surtout pour que des assets réellement publics restent chargeables, y compris par des pages isolées par COEP.
Quand le header est absent, le navigateur se comporte comme si cross-origin était
posé, donc n'importe quel site peut embarquer votre asset. Poser une valeur, c'est se
désengager de cela. Si le navigateur voit une valeur plus stricte que ce que la
requête autorise, le chargement échoue en erreur réseau. Notez que la requête part
quand même et que le serveur répond quand même ; le navigateur jette le corps de la
réponse au lieu de le remettre à la page. Dans Chromium, l'échec apparaît dans les
DevTools comme net::ERR_BLOCKED_BY_RESPONSE.
CORP protège contre plusieurs choses distinctes. Il garde votre ressource hors du processus d'une page attaquante, ce qui est la motivation de canal auxiliaire de type Spectre pour laquelle il a été relancé. Il bloque l'inclusion de script cross-site (XSSI). Et dans les navigateurs qui l'appliquent, soit la grande majorité, il empêche vos assets d'être affichés sur d'autres sites. C'est un contrôle d'embarquement, pas une protection de bande passante : les octets transitent quand même avant que le navigateur ne les écarte, et les clients non-navigateurs ignorent complètement le header.
Cross-Origin-Embedder-Policy, posé par la page
COEP est l'autre moitié. Une page envoie require-corp pour dire qu'elle ne chargera
une sous-ressource cross-origin que si cette ressource a explicitement accepté.
Cross-Origin-Embedder-Policy: require-corpVoici la partie qui piège les gens. Sous require-corp, un header CORP manquant ou
malformé est traité comme same-origin. Un asset cross-origin qui n'envoie aucun
CORP retombe par défaut sur same-origin du point de vue de l'embarqueur, donc la page
ne peut pas le charger. Ce défaut est exactement ce qui bloque les assets tiers non
étiquetés une fois que vous activez COEP. Chromium le suit même comme une raison de
blocage à part entière, distincte d'un simple décalage CORP : la console affiche
ERR_BLOCKED_BY_RESPONSE.NotSameOriginAfterDefaultedToSameOriginByCoep. Une ressource
accepte soit en envoyant Cross-Origin-Resource-Policy: cross-origin, soit en étant
récupérée via une vraie requête CORS (l'attribut crossorigin sur la balise).
COEP s'applique aussi aux iframes, au-delà des simples sous-ressources. Sous
require-corp, un document cross-origin embarqué doit envoyer son propre header COEP
compatible, sinon le cadre est bloqué. CORP sur la réponse du cadre ne suffit pas ; le
document encadré doit énoncer sa propre politique d'embedder.
Comment ils se combinent pour l'isolation cross-origin
La raison de se soucier des deux à la fois est l'isolation cross-origin. Quand une page
pose Cross-Origin-Opener-Policy: same-origin et Cross-Origin-Embedder-Policy: require-corp ensemble, le navigateur la marque cross-origin isolée. Cet état, lisible
en JavaScript via self.crossOriginIsolated, est la porte d'accès à
SharedArrayBuffer et aux timers haute résolution non bridés. COOP
gère le côté fenêtre (il place la page dans son propre groupe de contextes de
navigation et coupe les liens window.opener vers les pages cross-origin), et COEP
gère le côté sous-ressources. Les deux sont requis.
Cette porte est aussi la raison pour laquelle la plupart des sites qui envoient CORP
ont commencé à le faire. Les navigateurs ont désactivé SharedArrayBuffer début 2018
après Spectre. Firefox l'a réintroduit en 2020, conditionné à l'isolation
cross-origin ; Chrome l'avait réactivé plus tôt sur desktop sans cette condition, puis
a exigé l'isolation sur toutes les plateformes en 2021.
Tout ce qui a besoin de mémoire partagée aujourd'hui (threads wasm, ffmpeg.wasm,
éditeurs vidéo dans le navigateur) doit envoyer COEP, et COEP force à son tour chaque
asset cross-origin chargé par la page à porter CORP ou à passer par CORS. Vous ne
pouvez pas atteindre l'état isolé si vos propres assets et votre CDN restent
silencieux. Le
guide COOP et COEP de l'isolation cross-origin
parcourt la configuration complète.
Le piège Access-Control-Allow-Origin
Un correctif erroné courant consiste à ajouter Access-Control-Allow-Origin à une
ressource en s'attendant à satisfaire require-corp. Ce n'est pas le cas. Envoyer
Access-Control-Allow-Origin sur une réponse no-cors ne fait rien pour COEP. Pour
satisfaire require-corp, l'élément doit émettre une vraie requête CORS (l'attribut
crossorigin), ou la ressource doit envoyer un header CORP. Un header de réponse CORS
sur une requête qui n'a jamais été faite en mode CORS est ignoré.
credentialless, l'option à moindre friction
Cross-Origin-Embedder-Policy: credentialless est une alternative plus souple. Au lieu
d'exiger que chaque ressource cross-origin envoie CORP, elle retire les credentials
(pas de cookies, pas de certificats client) des requêtes de sous-ressources no-cors
cross-origin, de sorte que les réponses ne peuvent pas être des données personnalisées
dignes de protection. Elle atteint tout de même l'isolation cross-origin. Deux
limites : elle ne change que les sous-ressources no-cors, donc les requêtes en mode
CORS suivent les règles de require-corp et les iframes cross-origin ont toujours
besoin de leur propre header COEP. Et la prise en charge est partielle :
credentialless fonctionne dans les navigateurs Chromium et Firefox mais pas Safari,
donc require-corp reste le choix interopérable.
Testez avec Report-Only avant de forcer
COEP a un mode répétition. Envoyez Cross-Origin-Embedder-Policy-Report-Only: require-corp avec un paramètre report-to pointant vers un endpoint
Reporting-Endpoints,
et le navigateur émet un report coep
pour chaque chargement que le mode strict aurait bloqué, sans rien bloquer. Le corps du
report vous dit si l'échec venait d'un décalage corp, d'une navigation ou d'une
worker initialization, plus l'URL bloquée. Laissez-le tourner un moment, corrigez les
ressources signalées, puis basculez vers le header en mode strict. CentralCSP ingère
ces reports aux côtés des violations CSP, donc vous
voyez chaque asset qui aurait été bloqué sur du trafic réel avant vos utilisateurs.
CORP vs COEP côte à côte
| Cross-Origin-Resource-Policy | Cross-Origin-Embedder-Policy | |
|---|---|---|
| Qui le pose | La ressource (image, script, police, média) | La page qui embarque |
| Ce qu'il déclare | Qui peut me charger | Je ne charge que des ressources qui ont accepté |
| Valeurs | same-origin, same-site, cross-origin | require-corp, credentialless |
| Défaut quand absent | Traité comme cross-origin, n'importe qui peut embarquer (mais bascule sur same-origin sous le require-corp d'un embarqueur) | Aucune politique, isolation cross-origin désactivée |
| Mode d'échec | Un chargement bloqué est une erreur réseau, le corps n'est jamais livré | Les sous-ressources et cadres qui n'ont pas accepté sont bloqués |
Recommandation
Traitez les deux ensemble. Posez CORP sur vos propres ressources au niveau dont elles
ont besoin : same-origin pour les assets privés, same-site là où des sous-domaines
les consomment, et cross-origin uniquement sur les assets réellement publics. La
cheat sheet OWASP sur les headers HTTP recommande same-site comme référence
générale, avec require-corp sur les pages. Quand vous activez require-corp,
assurez-vous que chaque asset que cette page charge, vos propres fichiers et ceux de
votre CDN, envoie un header CORP que l'embarqueur acceptera, ou est récupéré avec
crossorigin. Recourez à credentialless quand changer chaque tierce partie n'est
pas praticable et que vous n'avez pas besoin de Safari, et utilisez le header
Report-Only pour trouver la casse avant de forcer.
Un scanner est le moyen rapide de voir lesquelles de vos réponses portent réellement CORP et quelles pages affirment COEP. Le scanner de headers de sécurité vérifie les deux sur tout votre site pour que vous repériez les manques avant qu'une page isolée ne se mette à échouer à charger ses assets.
FAQ
Ai-je besoin de CORP si j'ai déjà COEP ?
Oui, ils couvrent des directions opposées. COEP sur votre page ne fait rien pour
protéger vos propres ressources contre le chargement par d'autres sites ; seul CORP
sur ces réponses le fait. Et dès que votre page envoie COEP: require-corp, chaque
asset cross-origin qu'elle charge, y compris votre propre sous-domaine CDN, doit
envoyer CORP ou être récupéré avec CORS, parce qu'un header CORP manquant est traité
comme same-origin et bloqué.
COEP require-corp bloque-t-il les images ?
Oui. Un simple <img> pointant vers un hôte cross-origin est un chargement no-cors,
et sous require-corp il est bloqué (Chromium affiche net::ERR_BLOCKED_BY_RESPONSE)
sauf si le serveur d'images envoie Cross-Origin-Resource-Policy: cross-origin (ou un
same-site correspondant), ou si la balise utilise crossorigin et que le serveur
répond avec des headers CORS valides. Si vous ne contrôlez ni l'un ni l'autre, faites
transiter l'image par votre propre origine ou utilisez COEP: credentialless.
Quelle est la différence entre CORP et CORS ?
Ils vont dans des directions opposées. CORS est un opt-in côté serveur qui accorde un
accès en lecture cross-origin aux réponses des requêtes faites en mode CORS. CORP est
une restriction côté serveur qui retire l'embarquabilité par défaut des chargements
no-cors (images, scripts, médias que n'importe quelle page pouvait historiquement
inclure). CORP n'affecte que les requêtes no-cors, et les headers CORS sur une réponse
no-cors sont ignorés, ce qui explique pourquoi Access-Control-Allow-Origin seul ne
satisfait jamais require-corp.
Dois-je utiliser credentialless plutôt que require-corp ?
Une valeur COEP qui atteint l'isolation cross-origin sans exiger CORP de chaque tierce
partie. Les requêtes de sous-ressources no-cors cross-origin partent sans credentials
(pas de cookies, pas de certificats client), donc les réponses ne peuvent pas porter
de données personnalisées. Les requêtes en mode CORS suivent toujours les règles de
require-corp, et les iframes cross-origin ont toujours besoin de leur propre header
COEP. Elle fonctionne dans les navigateurs Chromium et Firefox mais pas Safari.
Articles liés
- COOP et COEP, l'isolation cross-origin expliquée
- Référence Cross-Origin-Opener-Policy
- Référence Cross-Origin-Resource-Policy
- Référence Cross-Origin-Embedder-Policy
- Le type de report coep
Sources
- WHATWG Fetch, Cross-Origin-Resource-Policy header
- WICG, COEP credentialless specification
- Chromium, CORP enforcement source
- Chromium, blocked-by-response reasons
- MDN, Cross-Origin-Resource-Policy header
- MDN, Cross-Origin Resource Policy guide
- MDN, Cross-Origin-Embedder-Policy header
- MDN, SharedArrayBuffer
- web.dev, Making your website cross-origin isolated
- Chrome developers, SharedArrayBuffer updates
- OWASP, HTTP headers cheat sheet