Le schéma blob dans la Content Security Policy
CentralCSP Team ·
Dernière mise à jour:
Si votre page crée un Web Worker ou lit un média depuis une URL blob:, la Content Security Policy (CSP) va le bloquer tant que vous n'avez pas listé blob: sur la bonne directive. La CSP est un header de réponse HTTP qui indique au navigateur quelles ressources une page a le droit de charger. Le piège qui surprend la plupart des gens : le mot-clé 'self' et le wildcard * ne correspondent pas à blob:. Vous devez l'autoriser nommément.
En résumé : blob: est une source de schéma, comme https: ou data:. Pour autoriser un worker construit à partir d'un blob, vous écrivez worker-src 'self' blob: ; pour autoriser un script blob, vous écrivez script-src 'self' blob:. Mais autoriser blob: dans un contexte de script laisse la page exécuter du code qu'elle fabrique à l'exécution, ce qui revient presque à rétablir 'unsafe-eval'. Autorisez-le donc là où vous devez le faire (workers, médias), et tenez-le à l'écart des directives de script partout où c'est possible.
Vous débutez avec ce header ? Démarrer avec la Content Security Policy couvre votre première politique avant que vous n'ajustiez les cas limites.
Qu'est-ce qu'une URL blob ?
Une URL blob: pointe vers un objet Blob ou File conservé en mémoire par le navigateur. JavaScript en crée une à l'exécution avec URL.createObjectURL(), et le résultat ressemble à blob:https://api-next.centralcsp.com/9d3a.... L'URL est opaque et éphémère, et elle n'existe que dans cette page. Vous ne pouvez pas l'héberger, l'autoriser par hôte, ni la verrouiller avec un hash.
Les pages utilisent les URL blob pour des tâches légitimes : générer un fichier à télécharger, diffuser de l'audio ou de la vidéo enregistrés, ou lancer un Web Worker à partir de code assemblé à la volée. Voici le cas du worker, celui sur lequel la CSP achoppe le plus souvent.
<script>
// Worker code assembled at runtime, then run from a blob: URL
const workerCode = `
self.onmessage = (event) => {
const sum = event.data.reduce((acc, value) => acc + value, 0);
postMessage(sum);
};
`;
const workerBlob = new Blob([workerCode], { type: "application/javascript" });
const workerUrl = URL.createObjectURL(workerBlob);
const worker = new Worker(workerUrl);
worker.onmessage = (event) => console.log("sum:", event.data);
worker.postMessage([1, 2, 3, 4, 5]);
</script>Avec une politique stricte en place, cet appel new Worker(workerUrl) échoue tant que la politique n'autorise pas blob:.
Quelle directive gouverne blob
La directive dépend de l'usage du blob.
Pour un worker, la directive worker-src s'applique aux scripts Worker, SharedWorker et ServiceWorker. Si worker-src est absent, le navigateur redescend une chaîne de repli : worker-src, puis child-src, puis script-src, puis default-src. Un worker construit à partir de URL.createObjectURL() est donc vérifié contre celle de ces directives qui est présente en premier. Si votre seule directive de script est script-src 'self' sans worker-src, le worker est vérifié contre script-src, et le blob: est bloqué.
Un worker bloqué n'est pas un échec en douceur. Le navigateur traite la requête refusée comme une erreur réseau fatale, donc le worker ne s'exécute tout simplement jamais.
Pour un script chargé depuis un blob (un <script src="blob:..."> ou tout fetch de blob typé comme script), la directive qui gouverne est script-src, avec un repli sur default-src.
Les médias et les images suivent le même motif de source de schéma sur leur propre directive :
Content-Security-Policy: media-src 'self' blob:Content-Security-Policy: img-src 'self' blob:Pourquoi self et le wildcard ne couvrent pas blob
C'est la partie qui surprend. Une URL blob: de même origine n'est pas couverte par le mot-clé 'self'. Même si le blob partage votre origine, vous devez quand même lister blob: explicitement.
Le wildcard * n'aide pas non plus. L'algorithme de correspondance de la liste de sources CSP exclut blob:, data: et filesystem: d'un simple *. Donc default-src * n'autorise pas blob:. Ces schémas sont traités à part parce que ce sont des schémas locaux qui produisent du contenu depuis la page elle-même plutôt que depuis un hôte réseau.
Cela signifie qu'une politique comme script-src 'self', ou même script-src *, bloquera un worker blob ou un script blob. Pour l'autoriser, nommez le schéma en tant que source de schéma :
Content-Security-Policy: worker-src 'self' blob:Notez que blob: s'écrit sans quotes, avec les deux-points finaux. C'est une source de schéma, pas un mot-clé, donc il ne prend jamais les quotes simples qu'utilisent 'self' ou 'unsafe-inline'.
Tous les principaux navigateurs se comportent ainsi aujourd'hui. (D'anciennes versions de Chrome ont un temps considéré que 'self' couvrait les scripts blob: de même origine, ce qui contredisait la spec ainsi que Firefox et WebKit. Chrome actuel exige blob: explicitement, vous pouvez donc compter sur la même règle partout.)
À quoi ressemble un blob bloqué dans les reports
Une ressource blob: refusée produit un report de violation CSP standard, sans forme particulière. Le champ effectiveDirective du report reflète la directive qui l'a bloquée, par exemple worker-src ou script-src. L'URL bloquée est généralement reportée comme le schéma blob plutôt que sous la forme de l'URL opaque complète, parce que le navigateur assainit les URI bloquées. Les champs d'un report de violation CSP expliquent ce que signifie chacune de ces valeurs. La chaîne blockedURL exacte qu'un navigateur reporte pour une ressource blob: peut varier, donc basez votre traitement sur effectiveDirective plutôt que sur l'URL.
Si vous ne collectez pas encore ces reports, démarrer avec le reporting CSP détaille le câblage d'un endpoint report-to pour que les blobs bloqués apparaissent au lieu d'échouer en silence. Un endpoint de reporting ressemble à https://<Endpoint-ID>.report.centralcsp.com.

Le risque côté script
Une page peut assembler du code arbitraire dans un Blob et l'exécuter comme worker ou comme script via son URL blob. Autoriser blob: sur script-src ou worker-src laisse donc la page exécuter du code qu'elle construit à l'exécution. En pratique, cela affaiblit la politique d'une manière comparable à 'unsafe-eval' : si un attaquant prend pied (via une faille XSS ou une dépendance compromise), il peut construire un blob malveillant et le charger comme script, et la politique le laisse passer parce que la source est blob:, et non parce que quelqu'un en a examiné le contenu.
<!-- If script-src allows blob:, this runs. Any script on the page could do it. -->
<script>
const code = "console.log('arbitrary code via blob:', document.domain)";
const blob = new Blob([code], { type: "text/javascript" });
const s = document.createElement("script");
s.src = URL.createObjectURL(blob);
document.body.appendChild(s);
</script>Le contenu d'un blob est généré à l'exécution, il est donc aussi difficile à auditer. Il n'y a pas de fichier statique à examiner, ni d'hôte ou de hash sur lequel le verrouiller. C'est le compromis que vous acceptez quand blob: figure dans une directive de script.
Comment tenir blob à l'écart des contextes de script
L'objectif est d'autoriser blob: seulement là où il ne peut pas exécuter de code, et de servir scripts et workers depuis de vraies URL que vous contrôlez.
- Servez les scripts de worker depuis un chemin statique plutôt qu'un blob. Un fichier en
/static/workers/task.jspeut être autorisé par hôte, et il survit à une politique plus stricte. - Autorisez vos propres scripts inline avec un nonce ou un hash plutôt qu'un schéma. Les valeurs nonce et hash vous permettent d'approuver des scripts précis par leur identité, ce que
blob:ne peut pas faire. - Réservez
blob:aux directives non exécutantes commemedia-srcouimg-srcquand votre application en a réellement besoin pour la lecture ou l'affichage d'images.
Un worker chargé depuis une URL statique n'a aucun besoin de blob :
<script>
const worker = new Worker("/static/workers/task.js");
worker.postMessage([1, 2, 3, 4, 5]);
</script>La politique qui en résulte limite l'exécution des scripts et des workers à votre origine, et n'autorise blob: que pour les médias :
Content-Security-Policy: default-src 'self';
script-src 'self';
worker-src 'self';
media-src 'self' blob:Si vous avez vraiment besoin d'un worker blob et ne pouvez pas le déplacer vers un fichier statique, cadrez l'autorisation au plus serré : mettez blob: sur worker-src uniquement, pour qu'il n'élargisse jamais ce que script-src accepte.
Déployez-la d'abord en Report-Only
Avant d'imposer une politique qui retire blob: des directives de script et de worker, faites-la tourner en mode observation pour voir ce qui casserait. Le header Content-Security-Policy-Report-Only reporte les violations sans rien bloquer. Surveillez les workers blob bloqués, corrigez les cas légitimes en servant le worker depuis une URL statique, puis basculez en mode enforce.
Quand vous auditez ce que votre site autorise déjà, notre scanner CSP montre la politique en vigueur sur une page et signale les endroits où blob: se trouve dans une directive risquée, pour que vous puissiez la resserrer avant qu'un attaquant ne le découvre.
Référence rapide
blob:est une source de schéma. Écrivez-le sans quotes, avec les deux-points :worker-src 'self' blob:.- Ni
'self'ni*ne correspondent àblob:. Autorisez-le nommément. - Les workers résolvent
worker-src, puischild-src, puisscript-src, puisdefault-src. Un worker bloqué est une erreur réseau fatale. - Les scripts résolvent
script-src, puisdefault-src. - Autoriser
blob:dans une directive de script revient presque à'unsafe-eval'. Tenez-le à l'écart descript-srcetworker-srcpartout où c'est possible ; servez plutôt les workers depuis des URL statiques.
Vous voulez durcir le reste du header ensuite ? Comment construire une CSP solide couvre les nonces, les hashes, et la suppression des mots-clés qui affaiblissent la politique.