Programmation sécurisée pour les développeurs JavaScript
JavaScript s’exécute partout où un navigateur le fait, ce qui en fait l’une des surfaces d’attaque les plus exposées dans le développement logiciel. La plupart des vulnérabilités ne proviennent pas d’exploits exotiques—elles proviennent de schémas prévisibles dans le code quotidien. Ce guide couvre les pratiques de programmation JavaScript sécurisée qui comptent le plus lorsque votre code s’exécute directement dans le navigateur.
Points clés à retenir
- Le XSS basé sur le DOM est l’une des vulnérabilités JavaScript les plus courantes—évitez de transmettre des données non fiables à des récepteurs comme
innerHTML,eval(), oudocument.write() - Utilisez
textContentau lieu deinnerHTMLpour les données fournies par l’utilisateur, et nettoyez avec DOMPurify lorsque vous devez réellement afficher du HTML - N’utilisez jamais
eval(),Function(), ousetTimeout/setIntervalbasés sur des chaînes avec des entrées dynamiques - Appliquez une Content Security Policy (CSP) avec des nonces ou des hachages et associez-la aux Trusted Types pour une défense en profondeur
- Évitez de stocker des jetons d’authentification ou des secrets dans
localStorageousessionStorage—privilégiez les cookiesHttpOnlypour les identifiants de session - Validez toujours
event.originlors du traitement des événementspostMessage - Auditez régulièrement votre arbre de dépendances et utilisez l’intégrité des sous-ressources (Subresource Integrity) pour les scripts chargés depuis un CDN
XSS basé sur le DOM : l’une des vulnérabilités JavaScript les plus courantes
La prévention du XSS en JavaScript commence par comprendre d’où il provient réellement. Le XSS basé sur le DOM se produit lorsque votre code lit à partir d’une source contrôlée par un attaquant—comme location.hash, document.referrer, ou URLSearchParams—et l’écrit dans la page de manière non sécurisée.
La règle fondamentale : ne jamais transmettre de données non fiables à un récepteur qui interprète du HTML ou exécute du code.
Récepteurs non sécurisés à éviter avec des données dynamiques :
// ❌ Tous ces éléments peuvent exécuter des scripts injectés
element.innerHTML = userInput
element.outerHTML = userInput
document.write(userInput)
eval(userInput)
setTimeout(userInput, 0) // forme chaîne uniquement
new Function(userInput)()
Alternatives sécurisées :
// ✅ Traite le contenu comme du texte, jamais comme du balisage
element.textContent = userInput
element.innerText = userInput
Lorsque vous devez réellement afficher du HTML—un éditeur de texte enrichi, par exemple—nettoyez d’abord avec DOMPurify :
// ✅ Rendu de texte enrichi sécurisé
element.innerHTML = DOMPurify.sanitize(userInput)
La même logique s’applique à setAttribute. Les attributs dynamiques comme href, src, et les gestionnaires d’événements (onclick, onload) peuvent exécuter du JavaScript. Tenez-vous-en aux attributs statiques et non exécutables lorsque vous travaillez avec des valeurs fournies par l’utilisateur.
L’exécution de code dynamique est toujours non sécurisée
eval(), Function(), et setTimeout/setInterval basés sur des chaînes ne sont pas seulement de mauvaises pratiques—ce sont des vecteurs directs d’injection de code. Il n’existe aucun moyen sûr de les utiliser avec des entrées non fiables.
Si vous analysez des données, utilisez JSON.parse(). Si vous avez besoin d’un comportement dynamique, utilisez des structures de données et une logique explicite plutôt que la génération de code à l’exécution.
Content Security Policy comme couche de défense
La Content Security Policy (CSP) limite les scripts qui peuvent s’exécuter et d’où ils peuvent être chargés. Une CSP stricte utilisant des nonces ou des hachages—plutôt que 'unsafe-inline'—réduit considérablement le rayon d’impact de tout XSS qui passerait à travers.
Associez la CSP aux Trusted Types dans les navigateurs compatibles pour appliquer des écritures DOM sécurisées au niveau de l’API. La prise en charge des Trusted Types par les navigateurs continue de s’étendre et peut être suivie sur webstatus.dev.
Discover how at OpenReplay.com.
API de navigateur sécurisées : cookies et stockage côté client
Les cookies contenant des identifiants de session doivent toujours porter les attributs HttpOnly (bloque l’accès JavaScript), Secure (HTTPS uniquement), et SameSite=Strict ou Lax (aide à atténuer le CSRF). Définir ces attributs côté serveur est plus fiable que de le faire depuis JavaScript.
localStorage et sessionStorage sont accessibles à tout script sur la page. Évitez d’y stocker des jetons d’authentification, des secrets de session ou des données utilisateur sensibles—une vulnérabilité XSS expose immédiatement tout ce qui se trouve dans le stockage.
Messagerie cross-origin avec postMessage
postMessage est utile mais facile à mal utiliser. Validez toujours l’origin des messages entrants avant d’agir sur leurs données :
window.addEventListener('message', (event) => {
// ✅ Toujours valider l'origine avant le traitement
if (event.origin !== 'https://trusted-origin.com') return
handleMessage(event.data)
})
Lors de l’envoi de messages, évitez d’utiliser '*' comme targetOrigin sauf si vous n’avez vraiment aucune destination fixe. Du côté de la réception, validez toujours event.origin pour vous assurer que le message provient d’un site de confiance. Plus de détails sur l’utilisation sécurisée sont couverts dans la documentation postMessage.
Sécurité de la chaîne d’approvisionnement JavaScript
La sécurité de la chaîne d’approvisionnement JavaScript est une préoccupation croissante. Un seul package compromis ou malveillant peut affecter des milliers d’applications. Étapes pratiques :
- Exécutez
npm auditou utilisez Snyk pour détecter les vulnérabilités connues dans les dépendances - Commitez votre fichier de verrouillage (
package-lock.jsonouyarn.lock) et traitez les changements inattendus comme un signal d’alarme - Utilisez des hachages Subresource Integrity (SRI) pour tous les scripts chargés depuis un CDN
- Auditez les nouveaux packages avant de les ajouter—vérifiez le nombre de téléchargements, l’activité de maintenance, et si le nom du package pourrait être une faute de frappe intentionnelle (typosquat)
Référence rapide : schémas sécurisés vs non sécurisés
| Non sécurisé | Alternative sécurisée |
|---|---|
innerHTML = userInput | textContent = userInput |
eval(str) | JSON.parse(str) |
setTimeout(str, n) | setTimeout(fn, n) |
Jeton dans localStorage | Cookie HttpOnly |
message sans vérification d’origine | Valider event.origin d’abord |
Conclusion
La plupart des vulnérabilités JavaScript suivent le même schéma : des données non fiables atteignent une API puissante sans validation. Prenez l’habitude de vous demander « d’où vient cette valeur, et que peut faire cette API avec ? » Cette question, appliquée de manière cohérente, détecte la majorité des problèmes avant leur mise en production.
FAQ
Pas toujours, mais il est non sécurisé chaque fois que vous lui transmettez des données provenant d'une entrée utilisateur ou de toute source externe. Si vous devez afficher du HTML dynamique, nettoyez-le d'abord avec une bibliothèque comme DOMPurify. Pour du contenu en texte brut, utilisez textContent à la place, qui n'interprète jamais le balisage ni n'exécute de scripts.
localStorage est accessible à tout JavaScript s'exécutant sur la page. Si un attaquant exploite une vulnérabilité XSS, il peut lire tout ce qui se trouve dans le stockage, y compris vos jetons. Les cookies HttpOnly sont un choix plus sûr car JavaScript ne peut pas du tout y accéder, ce qui limite les dommages causés par les attaques côté client.
Le XSS réfléchi implique une charge utile malveillante envoyée au serveur et renvoyée dans la réponse. Le XSS basé sur le DOM n'atteint jamais le serveur. Au lieu de cela, le JavaScript côté client lit à partir d'une source contrôlée par l'attaquant comme le fragment d'URL et l'écrit dans la page de manière non sécurisée. Les deux sont dangereux, mais le XSS basé sur le DOM est plus difficile à détecter côté serveur.
La CSP indique au navigateur quelles sources de scripts sont autorisées à s'exécuter. Une politique stricte utilisant des nonces ou des hachages bloque les scripts en ligne et les scripts externes non autorisés. Même si un attaquant injecte du balisage malveillant dans la page, le navigateur refuse de l'exécuter car il ne correspond pas à la politique.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.