Ce que fait unsafe-eval en CSP et comment le retirer
CentralCSP Team ·
Dernière mise à jour:
Si votre politique de sécurité du contenu (CSP) contient 'unsafe-eval', vous laissez la page transformer des chaînes arbitraires en code exécuté. La CSP est un header de réponse HTTP qui indique au navigateur quels scripts il a le droit d'exécuter, et par défaut elle bloque les fonctions qui compilent une chaîne en JavaScript exécutable. Le mot-clé 'unsafe-eval' désactive de nouveau ce blocage.
En résumé : ne déployez pas 'unsafe-eval' dans une directive de script. Trouvez ce qui en dépend (le plus souvent une fonctionnalité de framework ou un snippet injecté), passez à un build qui n'a pas besoin de compiler du code au runtime, et si vous n'avez besoin que de WebAssembly, utilisez plutôt le plus restreint 'wasm-unsafe-eval'. La suite de cet article explique ce que le mot-clé autorise, pourquoi il affaiblit la politique, où il a tendance à se glisser, et comment s'en passer en commençant par Report-Only.
Vous débutez avec la CSP ? Démarrer avec la politique de sécurité du contenu couvre d'abord les bases. Pour le problème connexe des scripts inline, voyez pourquoi ne jamais utiliser unsafe-inline en CSP.
Que fait 'unsafe-eval' ?
Par défaut, une CSP qui définit script-src bloque toutes les API qui compilent une chaîne en code. Ajouter 'unsafe-eval' à cette directive les réactive. Les fonctions qu'elle débloque sont :
eval("..."), qui exécute une chaîne comme du JavaScript.- Le constructeur
Function,new Function("a", "return a + 1"), qui construit une fonction à partir d'une source en chaîne. setTimeout("code", 0)etsetInterval("code", 0)quand le premier argument est une chaîne plutôt qu'une fonction.- La compilation et l'instanciation de WebAssembly, qui historiquement nécessitaient aussi
'unsafe-eval'.
Quand 'unsafe-eval' est absent, le navigateur lève une erreur sur chacune de ces opérations et signale une violation. Passer une fonction à setTimeout continue de fonctionner ; seule la forme en chaîne est concernée.
Pourquoi il affaiblit la politique
Tout l'intérêt d'une CSP stricte est que le navigateur n'exécute que le code que vous avez marqué comme de confiance, via un nonce ou un hash. Les API qui transforment des chaînes en code contournent cela. Une fois eval autorisé, toute chaîne contrôlée par l'attaquant qui atteint un sink eval devient du code exécuté, ce qui est précisément le point d'appui que recherchent la plupart des attaques de cross-site scripting (XSS).
Une chaîne d'attaque réelle ressemble à ceci : une saisie utilisateur arrive dans une valeur qu'une bibliothèque passe ensuite à eval ou new Function (un compilateur de templates, un évaluateur d'expressions, un parseur de config). Sans 'unsafe-eval', le navigateur refuse de la compiler. Avec 'unsafe-eval' présent, la chaîne injectée s'exécute avec tous les privilèges de la page, et la protection par nonce ou hash que vous avez mise en place n'y change rien.
La CSP est une défense en profondeur, pas un remplacement du traitement des entrées. Vous continuez à valider et à échapper. 'unsafe-eval' supprime la couche qui contient les dégâts quand quelque chose passe à travers.
Où 'unsafe-eval' se glisse
La plupart des équipes n'ajoutent pas 'unsafe-eval' volontairement. Il s'insinue parce qu'un outil en a besoin :
- Frameworks front-end en mode JIT. Angular avec le compilateur just-in-time compile les templates au runtime par génération de code. Vue avec le build à compilateur runtime (templates compilés dans le navigateur) fait de même. Les deux n'ont besoin de
'unsafe-eval'que dans ces modes. - Tag managers avec du JavaScript personnalisé. Les variables custom HTML et custom JavaScript de Google Tag Manager évaluent des chaînes, ce qui entraîne
'unsafe-eval'. Voyez faire tourner Google Analytics et Tag Manager sous une CSP stricte pour les contournements. - Anciens bundlers et builds de développement. Certains builds de développement et configurations de source-map utilisent
evalpour envelopper les modules. Les builds de production ne le font généralement pas, donc c'est souvent une fuite limitée au développement qui ne devrait jamais atteindre les headers de production. - Bibliothèques de graphiques, d'expressions et de templating. Une bibliothèque qui laisse les utilisateurs écrire des formules ou des templates peut les compiler avec
new Function. Vérifiez la bibliothèque avant d'autoriserevalpour elle. - WebAssembly. Compiler un module
.wasmcomptait autrefois comme unevaldu point de vue de la CSP, donc tout code utilisant WASM entraînait'unsafe-eval'.
Comment le retirer
La correction consiste presque toujours à cesser de compiler du code au runtime, pas à garder le mot-clé. Si vous ne pouvez pas encore retirer un sink eval, activez les Trusted Types pour que la compilation de chaîne restante doive d'abord passer par une policy.
Passez les frameworks en builds ahead-of-time (AOT)
Le build de production par défaut d'Angular est AOT : les templates sont compilés pendant le build, donc le navigateur ne compile rien et 'unsafe-eval' n'est pas nécessaire. Assurez-vous de ne pas déployer un build JIT en production. Pour Vue, utilisez le build runtime-only et précompilez les templates avec l'outillage de build plutôt qu'avec le compilateur runtime.
Content-Security-Policy: script-src 'self' 'nonce-r4nd0m' 'strict-dynamic'C'est l'objectif : une directive de script sans aucun 'unsafe-eval', qui fait confiance aux scripts via un nonce et 'strict-dynamic'. Pour savoir comment ce mot-clé propage la confiance, voyez le guide strict-dynamic.
Remplacez le code basé sur eval
Pour votre propre code, les formes en chaîne ont des remplacements directs :
// Before: needs 'unsafe-eval'
setTimeout("doWork()", 1000); // [!code --]
const add = new Function("a", "b", "return a + b"); // [!code --]
// After: works under a strict policy, no 'unsafe-eval'
setTimeout(() => doWork(), 1000); // [!code ++]
const add = (a, b) => a + b; // [!code ++]Passez une fonction à setTimeout et setInterval. Remplacez new Function par une vraie fonction ou une petite table de correspondance d'opérations connues. Parsez la config JSON avec JSON.parse, jamais eval.
Déplacez l'évaluation des templates ou expressions au moment du build
Si une bibliothèque compile des templates ou des expressions dans le navigateur, passez à son mode précompilé ou précalculez les valeurs pendant votre build, de sorte que rien n'atteigne un sink eval au runtime.
L'option plus restreinte, 'wasm-unsafe-eval'
Si la seule raison pour laquelle vous aviez besoin de 'unsafe-eval' était WebAssembly, il existe un mot-clé bien plus restreint. 'wasm-unsafe-eval' autorise la compilation et l'instanciation de modules WASM mais ne réactive pas eval, new Function ni les timers à chaîne. C'est le bon choix quand vous déployez un module WASM et voulez que tout le reste reste bloqué.
Content-Security-Policy: script-src 'self' 'wasm-unsafe-eval'Utilisez 'wasm-unsafe-eval' au lieu de 'unsafe-eval' chaque fois que WASM est votre seul besoin. Il est largement pris en charge par les navigateurs actuels. 'unsafe-eval' autorise bien la compilation WebAssembly, mais il réactive aussi eval, new Function et les timers à chaîne, donc l'utiliser juste pour WASM est plus large que nécessaire. 'wasm-unsafe-eval' est le mot-clé plus restreint qui n'autorise que la compilation WASM et laisse tout le reste bloqué.

Un chemin de migration sûr hors de 'unsafe-eval'
Retirer 'unsafe-eval' peut casser tout ce qui s'appuyait discrètement dessus, alors avancez par étapes et surveillez les reports avant d'appliquer l'enforcement. C'est la même approche Report-Only d'abord que mode enforce vs Report-Only.
- Trouvez l'usage d'eval. Cherchez dans votre bundle et vos dépendances
eval(,new Function(, et lessetTimeout/setIntervalà chaîne. L'évaluateur CSP signale quand une politique en production porte encore'unsafe-eval'. - Corrigez chaque cas. Passez les frameworks en AOT, remplacez les timers à chaîne et
new Function, et faites passer WASM à'wasm-unsafe-eval'. - Testez en Report-Only. Déployez la politique sans
'unsafe-eval'sur le headerContent-Security-Policy-Report-Only. Le navigateur ne bloque rien, il signale seulement ce qu'il bloquerait, vous voyez donc chaque sink eval restant sur le trafic réel. - Appliquez l'enforcement. Quand le Report-Only est silencieux, déplacez la même politique vers le header
Content-Security-Policyqui applique l'enforcement.
CentralCSP collecte ces reports Report-Only pour vous, les regroupe, et montre les scripts qui tournent sur chaque page, pour que vous puissiez identifier quelle dépendance appelle encore eval avant de basculer en enforcement. C'est le cœur de la suite CSP. Vous pouvez démarrer un essai gratuit et y pointer un header Report-Only pour suivre la migration.
Pour la référence complète des directives et des mots-clés, voyez la référence de la politique CSP.
Questions fréquentes
Peut-on garder 'unsafe-eval' sans risque ?
Non. Il réactive la compilation de chaîne en code pour n'importe quelle chaîne, ce qui est le comportement dont abusent la plupart des attaques XSS. Retirez-le et utilisez des builds AOT, des fonctions classiques, et 'wasm-unsafe-eval' pour WebAssembly.
Quelle est la différence entre 'unsafe-eval' et 'wasm-unsafe-eval' ?
'unsafe-eval' autorise eval, new Function et les timers à chaîne ainsi que WebAssembly. 'wasm-unsafe-eval' n'autorise que la compilation WebAssembly et laisse le reste bloqué, c'est donc le choix plus restreint et plus sûr quand WASM est tout ce dont vous avez besoin.
Angular a-t-il besoin de 'unsafe-eval' ?
Seulement en mode just-in-time. Le build AOT de production par défaut compile les templates au moment du build, il n'a donc pas besoin de 'unsafe-eval'. Vérifiez que vous ne déployez pas un build JIT en production.