Chaque script tiers chargé sur votre page de paiement, de connexion ou d'administration s'exécute avec les mêmes privilèges que l'utilisateur qui l'a chargé. Si l'hôte qui sert ce script est compromis, l'attaquant obtient lui aussi ces privilèges, un peu comme avec une XSS. La correction commence par savoir quels scripts sont chargés, puis par supprimer ce qui peut l'être et par épingler ou restreindre le reste.
Savoir ce qui s'exécute sur les pages sensibles
La fiche OWASP sur le JavaScript tiers identifie trois risques : la perte de maîtrise des modifications apportées à votre application cliente, l'exécution de code arbitraire sur les systèmes des clients et la fuite d'informations sensibles vers des tiers. Parmi ses exemples figurent l'incident Ticketmaster de 2018 et l'enquête de ClearSky sur Magecart et le vol de données de paiement.
Construisez l'inventaire page par page, en commençant par le paiement, la connexion et l'administration. Pour chaque script, notez l'URL, le responsable métier, la raison de sa présence et son mode d'arrivée. OWASP décrit trois modes de livraison : du code fourni par l'éditeur collé dans la page, une balise <script src> pointant vers l'éditeur, et une requête indirecte via un tag manager.
Le tag manager est le moins visible. Il peut renvoyer des scripts de plusieurs éditeurs, et le personnel peut modifier ce qu'il renvoie depuis une interface web, sans toucher à votre code. Les changements sont en production dès le chargement de page suivant, et le code est généralement obfusqué.
Le code source HTML ne montrera pas ces scripts. Ouvrez les outils de développement, passez à l'onglet Network, parcourez le tunnel de paiement et la connexion, puis listez chaque hôte qui sert un script.
Supprimer d'abord, héberger en miroir ensuite
Demandez au responsable de chaque entrée si elle a vraiment sa place sur cette page. Un tag marketing sur une page de connexion ou un panneau d'administration trouve rarement une bonne justification.
Pour les scripts conservés, OWASP cite le miroir interne comme mesure de défense. Vous hébergez vous-même une copie relue, de sorte qu'un tiers ne peut pas la modifier, et l'éditeur ne reçoit plus l'adresse IP, le référent ni les cookies du visiteur. Le prix à payer est l'entretien : une copie miroir ne bénéficie pas des correctifs de l'éditeur, donc quelqu'un doit se charger de la relire et de la mettre à jour.
Les éditeurs abandonnés posent un problème à part. Si le domaine qui héberge un script expire parce que l'éditeur a fait faillite ou abandonné le projet, quelqu'un peut le réenregistrer et y publier du code malveillant. Un éditeur disparu dans votre inventaire constitue déjà un constat en soi.
Épingler ce que vous gardez avec Subresource Integrity
Subresource Integrity (SRI) permet à la page d'indiquer le hash auquel un fichier doit correspondre. Le navigateur compare le fichier téléchargé à ce hash avant d'exécuter un script ou d'appliquer une feuille de style, et renvoie une erreur réseau en cas de différence. Le guide SRI de MDN donne la commande de génération :
cat FILENAME.js | openssl dgst -sha384 -binary | openssl base64 -A
Préfixez le résultat par sha384- et placez-le dans l'élément :
<script src="https://cdn.example.com/script.js"
integrity="sha384-..."
crossorigin="anonymous"></script>
Le balisage impose quelques exigences :
- L'attribut
crossoriginest obligatoire. Le navigateur refuse SRI sur les requêtesno-cors, donc une balise qui en est dépourvue échouera systématiquement. - Le CDN doit envoyer un en-tête
Access-Control-Allow-Origin. Beaucoup envoient*. - Vous pouvez indiquer plusieurs hashes. Le navigateur n'utilise que ceux de l'algorithme le plus fort présent (SHA-256, SHA-384, SHA-512 par ordre croissant) et accepte le fichier si l'un d'eux correspond, ce qui vous permet d'autoriser deux versions pendant une mise à niveau. La spécification du W3C décrit le même comportement.
SRI convient aux fichiers qui ne changent pas tant que vous ne les modifiez pas. Un script que l'éditeur modifie à sa guise échoue à la vérification après chaque modification, et un conteneur de tag manager est précisément ce type de script. Limitez les personnes autorisées à y publier et tenez-le à l'écart de vos pages les plus sensibles.
Restreindre avec Content Security Policy
SRI dit ce qu'un fichier donné doit contenir. Une Content Security Policy (CSP) dit d'où les scripts peuvent provenir. Le guide CSP de MDN recommande de la livrer dans l'en-tête Content-Security-Policy sur toutes les réponses, pas seulement sur le document principal.
Un point de départ pour une page sensible liste les hôtes que vous utilisez réellement :
Content-Security-Policy: script-src 'self' cdn.example.com; object-src 'none'
Dès qu'une directive script-src ou default-src existe, le JavaScript inline est bloqué sauf si vous l'autorisez. MDN recommande d'éviter 'unsafe-inline', car il ruine une bonne partie de l'intérêt d'une CSP. Si vous avez besoin de scripts inline, utilisez un nonce : généré à chaque réponse, imprévisible, inséré par un template. Pour les pages statiques, MDN suggère plutôt des hashes, et les scripts externes ont alors aussi besoin de l'attribut integrity.
OWASP le dit sans détour : une data layer n'est pas un bac à sable, car les conteneurs de tag manager peuvent exécuter du JavaScript et du HTML personnalisés. Restreignez les droits de publication et relisez les changements comme du code applicatif.
Repérer les lacunes sans casser le site
L'en-tête Integrity-Policy fait bloquer par le navigateur les scripts dépourvus d'attribut integrity ou chargés en mode no-cors. Commencez par la version report-only :
Integrity-Policy-Report-Only: blocked-destinations=(script), endpoints=(integrity-endpoint)
Le endpoint nécessite un en-tête Reporting-Endpoints correspondant. Les violations arrivent sous forme de rapports de type integrity-violation, qui nomment l'URL bloquée. Les scripts épinglés disparaissent des rapports, ils ne constituent donc pas un inventaire complet des scripts.
Lorsqu'aucun rapport n'arrive, MDN indique qu'il est possible d'activer le blocage sans régression visible pour les utilisateurs. Un conteneur de tag manager que vous ne pouvez pas épingler continue de générer des rapports : les pages qui en chargent un restent donc en report-only. Vous pouvez inspecter les en-têtes envoyés par vos pages avec notre vérificateur d'en-têtes HTTP.
Par où commencer
- Construisez l'inventaire des pages de paiement, de connexion et d'administration à partir de l'onglet Network, en notant pour chaque script le responsable, la finalité et le mode de livraison.
- Supprimez les entrées que personne ne peut justifier, surtout sur les pages de connexion et d'administration, et signalez tout éditeur dont le domaine semble abandonné.
- Hébergez en miroir les fichiers que vous acceptez de maintenir et de relire. Épinglez chaque script conservé : générez des hashes SHA-384, ajoutez
integrityetcrossorigin="anonymous", puis testez en préproduction. - Déployez
Integrity-Policy-Report-Onlypour les scripts et lisez les rapports. N'appliquezIntegrity-Policyque lorsque chaque script de la page possède un attributintegrity, et laissez les pages avec tag manager en report-only. - Testez en préproduction une
Content-Security-Policyavecscript-srcetobject-src 'none', car elle bloque les scripts inline. Restreignez les droits de publication dans votre tag manager. Pour un contrôle externe, un test d'intrusion peut couvrir ces pages.
