Tous les articles

Le schéma data dans la Content Security Policy

CentralCSP Team ·

Dernière mise à jour:

Le schéma data: permet à une URL de transporter son propre contenu en inline : data:image/png;base64,... contient à lui seul une image entière, sans serveur à interroger. Dans une politique de sécurité du contenu (CSP), vous pouvez autoriser ce schéma directive par directive. Pour les images et les polices, c'est pratique et courant. Pour les scripts, cela donne à un attaquant un moyen d'exécuter du code sans le moindre hôte externe.

Cet article montre où data: a sa place dans une politique, où il n'en a pas, et pourquoi la différence compte.

À quoi ressemble le schéma data dans une CSP

data: est une source de schéma. La grammaire attend le nom du schéma suivi de deux-points : vous écrivez donc data: tel quel, avec le deux-points final et sans guillemets, contrairement aux sources par mot-clé comme 'self'.

L'usage courant et raisonnable concerne les images et les polices en inline :

Content-Security-Policy: img-src 'self' data:; font-src 'self' data:

Cela autorise img-src et font-src à charger des ressources depuis votre propre origine et depuis des data URI en inline, ce qui permet l'affichage des icônes en base64, des petites images de remplacement et des polices embarquées.

Chaque directive est indépendante. Une directive n'autorise data: que si vous l'y écrivez, et il ne s'y glisse ni par un fallback ni par un wildcard (nous y revenons plus bas).

Pourquoi data est sûr pour les images mais dangereux pour les scripts

Une image data: ne peut pas exécuter de code. Ce sont des octets que le navigateur décode et affiche. Un script data: est différent : l'URL transporte le corps du script, donc toute la charge utile tient dans l'expression de source.

Si script-src autorise ce schéma, un attaquant capable d'injecter une seule balise <script> n'a plus rien à héberger. Le code tient dans l'URL :

<script src="data:text/javascript,alert(document.domain)"></script>
<script src="data:;base64,YWxlcnQoMSk="></script>

Les versions actuelles de Chrome, Firefox et Safari exécutent les scripts data: externes, donc c'est un vecteur d'exécution opérationnel aujourd'hui, pas une bizarrerie héritée du passé. Les analyses de risque présentent data: dans script-src comme un contournement de CSP. La spécification, elle, se contente de définir la correspondance des schémas et n'en fait pas un avertissement XSS. Voyez donc ce danger comme une recommandation de durcissement, pas comme une règle de la spec.

Une précision s'impose. Les navigateurs bloquent la navigation de premier niveau vers une URL data:, un changement anti-phishing plus ancien, mais cela ne bloque pas les sous-ressources data: comme <script src="data:..."> ou <img src="data:...">. Le script se charge en sous-ressource dans la propre origine de la page, donc le blocage de navigation ne s'applique pas.

Le wildcard ne couvre pas data

On suppose naturellement que script-src * autorise tout, y compris data:. Ce n'est pas le cas. En CSP Level 3, un simple * ne correspond à une URL que lorsque celle-ci utilise un schéma HTTP ou HTTPS (plus le même schéma que le document protégé). Les schémas data:, blob: et filesystem: sont exclus et doivent être listés explicitement.

Content-Security-Policy: script-src *

Cette politique n'autorise pas les scripts data:. Il faudrait l'ajouter vous-même pour qu'ils s'exécutent, et c'est précisément pour ça qu'il ne faut pas le faire.

Cette exclusion est un correctif de sécurité délibéré. Les navigateurs ont restreint la correspondance de data: après la confirmation d'un contournement réel par wildcard, et ils se comportent tous de la même façon aujourd'hui.

Où autoriser data, et où ne pas le faire

Décidez directive par directive, selon que la directive peut ou non mener à l'exécution de code :

  • img-src, font-src et media-src : autoriser data: est légitime et très répandu pour les ressources en base64 en inline.
  • script-src : n'autorisez pas data:. C'est un vecteur d'exécution XSS, comme montré plus haut.
  • object-src et frame-src : risqués également, puisque des plugins ou des frames data: peuvent transporter du contenu actif.
  • style-src : déconseillé. Une feuille de style data: contrôlée par un attaquant se charge comme une sous-ressource et s'applique, ce qui ouvre un vrai vecteur d'injection et d'exfiltration par CSS (mais pas d'exécution de code).
  • default-src : particulièrement néfaste. En l'absence de script-src, c'est default-src qui régit les scripts, donc default-src data: permet de fait les scripts data:.

Une grande partie des sites autorisait historiquement data: sous script-src, frame-src ou object-src (ou default-src lorsque script-src était absent), et c'est pourquoi cela reste un constat fréquent.

L'alternative plus sûre pour les scripts

Le correctif n'est pas de trouver une manière prudente d'autoriser data: dans script-src. C'est d'arrêter complètement d'autoriser les scripts par schéma ou par hôte, et de passer au nonce ou au hash, avec 'strict-dynamic'.

Les recommandations de Chrome et de Google sont explicites : les allowlists par hôte et par schéma offrent peu de protection, et un nonce ou un hash plus 'strict-dynamic' est le modèle à adopter. Une politique stricte minimale ressemble à ceci :

Content-Security-Policy: script-src 'nonce-r4nd0m' 'strict-dynamic';
    object-src 'none';
    base-uri 'none'

Avec un nonce en place, le navigateur ne fait confiance qu'aux scripts portant ce nonce et aux scripts que ces scripts de confiance chargent. Un script data: sans nonce correspondant est rejeté, donc le contournement se referme même si un attaquant injecte une balise. Pour l'argumentaire général contre ce genre d'ouverture dans la politique, voyez pourquoi supprimer 'unsafe-inline' compte et la démonstration plus complète dans comment construire une CSP solide.

Si vous avez besoin de quelques images ou polices en base64 en inline, gardez data: cantonné à img-src et font-src et laissez-le hors de tout ce qui touche au script ou au style.

Repérer data dans votre propre politique

Procédez prudemment : placez d'abord la politique candidate sur le header Content-Security-Policy-Report-Only, surveillez les reports, et ne l'appliquez qu'une fois qu'ils se taisent. Pour savoir où en est votre politique en production aujourd'hui, passez-la dans l'évaluateur CSP, qui signale data: dans script-src et d'autres sources faibles afin que vous sachiez quoi resserrer avant qu'elles ne deviennent une porte d'entrée.

L'évaluateur lit la politique que vous livrez déjà. Ce qu'il ne peut pas vous dire, c'est quelles URI data: vos pages chargent réellement, car cela n'apparaît qu'à l'exécution. CentralCSP collecte les reports csp-violation issus de vraies visites et les regroupe par URI bloquée, si bien qu'en retirant data: de script-src, vous voyez exactement quels payloads inline cessent de se charger, et si certains étaient les vôtres.

Articles liés