Tous les articles

script-src-elem contre script-src-attr, quelle directive CSP contrôle quoi

CentralCSP Team ·

Dernière mise à jour:

script-src-elem régit les éléments <script>, externes et inline. script-src-attr régit les attributs event handler inline comme onclick. Les deux sont des découpages plus fins de script-src, et les deux retombent sur elle. La plupart des sites ne les posent jamais séparément, mais connaître ce découpage explique beaucoup de rapports de violation déroutants de la politique de sécurité du contenu (CSP).

La chaîne de fallback

La CSP résout les contrôles de script du spécifique au général. Quand le navigateur doit décider si un élément <script> peut s'exécuter, il cherche script-src-elem. Quand il décide si un event handler inline peut s'exécuter, il cherche script-src-attr. Si la directive spécifique est absente, la vérification retombe, d'abord sur script-src, puis sur default-src, en suivant la liste de fallback définie par CSP Level 3.

script-src-elem  ->  script-src  ->  default-src
script-src-attr  ->  script-src  ->  default-src

Ainsi une politique avec seulement script-src contrôle quand même les deux, éléments et handlers inline, parce que les deux retombent sur elle. Vous recourez aux directives granulaires quand vous voulez traiter les deux cas différemment.

Une règle compte plus que le schéma ne le laisse penser : le fallback est un remplacement, pas une fusion. Si votre politique contient script-src-elem, le navigateur l'utilise seul pour les vérifications d'éléments et ignore complètement script-src pour cette décision. Les sources listées dans script-src ne sont pas reprises. Une politique comme script-src 'self' cdn.example; script-src-elem 'nonce-abc' bloquera un script venant de cdn.example s'il ne porte pas le nonce, parce que dès que script-src-elem existe, elle devient le seul règlement pour les éléments.

script-src-elem contre script-src-attr

script-src-elemscript-src-attr
Contrôleéléments <script> (externes et inline)attributs event handler inline (onclick, ...)
Retombe surscript-src, puis default-srcscript-src, puis default-src
NoncesS'appliquentNe s'appliquent pas (aucun attribut pour en porter un)
HashesS'appliquent au contenu <script> inlineExigent 'unsafe-hashes' pour matcher un handler
URL javascript:Régies ici (selon CSP Level 3)Pas régies ici
Valeur stricte typique'nonce-...' 'strict-dynamic''none'

Le support navigateur n'est plus une raison de les éviter. Blink a livré les deux directives des années avant les autres ; WebKit a suivi, et Gecko a été le dernier moteur à les ajouter. Les trois prennent désormais en charge script-src-elem, script-src-attr et 'unsafe-hashes', et les navigateurs plus anciens qui ne reconnaissent pas les directives granulaires continuent simplement d'utiliser votre script-src, donc le fallback sert aussi de filet de compatibilité.

script-src-elem, la directive des éléments

script-src-elem décide quels éléments <script> le navigateur exécutera. Cela couvre à la fois les scripts externes (<script src="...">) et les blocs <script> inline.

C'est là que s'appliquent les nonces et les hashes. Une source 'nonce-...' correspond à un <script> qui porte le même attribut nonce, et un hash 'sha256-...' correspond à un <script> inline dont le contenu exact donne ce hash. 'strict-dynamic' vit aussi ici : il laisse un script de confiance par nonce charger d'autres scripts, comme traité dans ce que fait strict-dynamic.

Content-Security-Policy: script-src-elem 'nonce-r4nd0m' 'strict-dynamic'

script-src-elem accepte les mêmes expressions de source que script-src. Un détail qui surprend souvent est celui des URL javascript:. CSP Level 3 vérifie une navigation javascript: contre script-src-elem, pas contre script-src-attr, même si elle a des airs d'attribut quand elle se trouve dans un href. Cette vérification est aussi le seul endroit où 'unsafe-hashes' compte côté élément : un hash ne peut correspondre à une URL javascript: que si ce mot-clé est présent.

script-src-attr, la directive des handlers inline

script-src-attr décide si les attributs event handler inline peuvent s'exécuter : onclick, onerror, onload, et le reste. Elle ne touche pas du tout aux éléments <script>.

Les nonces ne s'appliquent pas à un attribut (il n'y a nulle part où mettre le nonce), donc le seul moyen d'autoriser un handler est 'unsafe-inline', ou un hash du code du handler combiné à 'unsafe-hashes'. Le hash seul ne suffit pas : faire correspondre un hash à un event handler ou à un attribut style exige le mot-clé supplémentaire 'unsafe-hashes', qui rouvre la surface des handlers inline. C'est pourquoi le correctif plus propre est de retirer les handlers inline et de les câbler avec addEventListener, comme traité dans pourquoi ne jamais utiliser unsafe-inline.

Il y a un second piège ici. 'unsafe-inline' est ignoré dès qu'un nonce ou un hash apparaît dans la même liste de sources. Donc script-src 'nonce-abc' 'unsafe-inline' n'autorise pas discrètement vos handlers onclick ; le nonce désactive le mot-clé 'unsafe-inline', et les handlers restent bloqués. Si vous devez réellement autoriser des handlers à côté d'une politique à nonce, l'autorisation doit vivre dans sa propre directive script-src-attr.

Content-Security-Policy: script-src-attr 'none'

Un exemple concret

Prenez une page avec trois choses : un script externe, un bloc <script> inline, et un bouton avec un handler onclick inline.

<script src="/app.js" nonce="r4nd0m"></script>
<script nonce="r4nd0m">init();</script>
<button onclick="save()">Save</button>

Sous une seule directive, tout est jugé par script-src :

Content-Security-Policy: script-src 'nonce-r4nd0m'

Les éléments <script> externe et inline s'exécutent parce qu'ils portent le nonce. Le handler onclick est bloqué, parce qu'un nonce ne peut pas correspondre à un attribut. Le rapport de violation nomme script-src-attr comme directive effective, même si vous n'avez écrit que script-src, parce que c'est la directive spécifique qui régit un handler inline.

Maintenant découpez les deux :

Content-Security-Policy:
    script-src-elem 'nonce-r4nd0m' 'strict-dynamic';
    script-src-attr 'none'

Même résultat, mais désormais chaque surface rend compte contre sa propre directive. Un <script> bloqué produit une violation avec script-src-elem comme directive effective ; un handler bloqué en produit une avec script-src-attr. Le découpage rend les rapports non ambigus sur la surface qui a échoué, ce qui est utile quand vous resserrez une politique et voulez voir les handlers inline séparément des éléments script.

Lire les rapports de violation

La directive granulaire est ce qui apparaît dans votre télémétrie, que vous l'ayez posée ou non. CSP Level 3 impose au navigateur de rapporter la directive effective de la vérification, donc une violation de handler inline dit toujours script-src-attr. Dans un rapport csp-violation de la Reporting API le champ est effectiveDirective ; le JSON legacy de report-uri utilise effective-directive, plus violated-directive comme alias historique. Le détail champ par champ est dans ce que signifie chaque champ de rapport de violation CSP.

Chromium explicite aussi le fallback dans la console. Un handler bloqué sous une politique script-src simple journalise un message se terminant par "Note that 'script-src-attr' was not explicitly set, so 'script-src' is used as a fallback." Quand vous voyez script-src-attr dans un rapport, lisez-le comme "un attribut event handler inline a été bloqué", puis décidez de corriger le markup ou de l'autoriser en connaissance de cause.

Cette distinction est la raison pratique de collecter les rapports plutôt que de lire la console. CentralCSP regroupe les rapports csp-violation entrants par effectiveDirective, si bien que script-src-elem et script-src-attr occupent des lignes distinctes même quand votre politique ne définit que script-src. Les éléments bloqués et les gestionnaires inline bloqués appellent des correctifs différents, et les séparer est ce qui vous dit si vous avez un problème de script tiers ou un problème de markup.

La colonne Directive montrant script-src-elem et script-src-attr sur des lignes distinctes

Recommandation

Restez simple. Déployez un script-src strict bâti sur un nonce plus 'strict-dynamic', et ajoutez script-src-attr 'none' pour supprimer purement et simplement les event handlers inline. Cette seule directive supplémentaire retire toute une surface d'injection et n'affecte pas vos éléments <script>, que le nonce régit déjà. Recourez à script-src-elem uniquement quand vous avez réellement besoin de règles différentes pour les scripts d'éléments et les handlers inline ; pour la plupart des sites, la politique ci-dessous, la CSP stricte OWASP plus script-src-attr 'none', suffit. Vérifiez la politique finale avec le évaluateur CSP.

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

Le même découpage existe pour les styles : style-src-elem couvre les éléments <style> et les liens de feuilles de style, style-src-attr couvre les attributs style inline, et les deux retombent sur style-src de la même manière.

FAQ

Pourquoi mon rapport CSP dit-il script-src-attr alors que je ne l'ai jamais posée ?

Parce que le navigateur rapporte la directive qui régit la vérification, pas celle que vous avez écrite. effectiveDirective: script-src-attr signifie qu'un attribut event handler inline (onclick, onerror, ...) a été bloqué ; votre script-src n'a été consulté que comme fallback. Corrigez le handler en le déplaçant vers addEventListener, ou autorisez-le en connaissance de cause via script-src-attr. Les extensions de navigateur qui injectent des handlers sont une autre source fréquente de ce bruit.

Pourquoi mon nonce ne fonctionne-t-il pas pour les handlers onclick ?

Les nonces ne peuvent jamais autoriser un event handler inline. La spécification ne consulte les nonces que pour les éléments <script> et <style>, et un attribut n'a nulle part où porter un nonce. Ajouter 'unsafe-inline' à côté du nonce n'aide pas non plus, parce qu'un nonce ou un hash dans la liste de sources fait ignorer 'unsafe-inline'. Réécrivez le handler avec addEventListener, ou autorisez le code exact avec script-src-attr 'unsafe-hashes' 'sha256-...'.

Quelle est la différence entre script-src et script-src-elem ?

script-src-elem est le sous-ensemble limité aux éléments : elle régit les éléments <script> et rien d'autre, tandis que script-src couvre aussi les event handlers inline et l'exécution de type eval. La référence script-src-elem donne le détail complet.

Dois-je poser script-src-elem ?

Non. Quand elle est absente, script-src couvre les éléments script, et default-src les couvre si script-src est absente aussi. Posez-la uniquement quand éléments et handlers ont besoin de règles différentes, et rappelez-vous que la poser remplace script-src pour les vérifications d'éléments au lieu de fusionner avec elle, donc elle doit lister toutes les sources dont vos scripts ont besoin.

Que fait unsafe-hashes en CSP ?

'unsafe-hashes' est un mot-clé de CSP Level 3 qui permet aux sources hash de correspondre aux event handlers inline, aux attributs style inline et aux navigations javascript:, que les hashes simples ne matchent jamais. Il est plus sûr que 'unsafe-inline' parce que seul le code haché exact peut s'exécuter, mais plus faible que retirer les handlers, puisque le code haché est alors autorisé dans n'importe quel handler de la page.

Lectures liées

Sources