Chapitre 8.3 — Clickjacking & open redirect
⏱️ TL;DR — Deux attaques qui n’injectent pas de code mais détournent l’interface et la navigation. Le clickjacking charge ton site dans une iframe invisible posée sur un leurre : la victime croit cliquer sur le leurre, mais son clic atteint ton interface (valider un paiement, changer un réglage). Parade : interdire la mise en iframe de ton site par d’autres origines —
X-Frame-Options: DENYet/ou CSPframe-ancestors 'none'. L’open redirect exploite une redirection dont la destination vient de l’utilisateur (/go?url=...) sans validation : l’attaquant fabrique un lien vers ton domaine (de confiance) qui rebondit vers un site de phishing. Parade : valider la cible (allow-list, redirections internes uniquement).
🎯 Objectifs
- Comprendre le clickjacking (iframe superposée) et son impact.
- Interdire la mise en iframe de son site (
X-Frame-Options,frame-ancestors). - Comprendre l’open redirect et pourquoi il sert le phishing.
- Valider les cibles de redirection (allow-list, chemins internes).
Clickjacking : le clic détourné
Le clickjacking (détournement de clic) consiste à superposer ton site — chargé dans une iframe transparente — par-dessus une page leurre attirante (« Cliquez pour gagner ! », un faux bouton de jeu). L’iframe est rendue invisible (opacité nulle) et positionnée pour que le bouton réel et sensible de ton site (valider un virement, supprimer un compte, accorder une permission) se trouve pile sous le curseur au moment où la victime clique le leurre.
Couche visible (leurre) : [ Cliquez ici pour continuer ]
Couche invisible (iframe) : [ Valider le virement de 500€ ] <- le vrai clic va iciLa victime, authentifiée sur ton site (l’iframe charge ta page avec sa session), clique « en confiance » sur le leurre — mais son clic actionne l’action sensible de ton interface. C’est une forme d’abus de confiance visuelle : rien n’est « piraté » techniquement, on trompe l’œil de l’utilisateur.
La parade : interdire l’encadrement
Puisque l’attaque exige de charger ton site dans une iframe, la parade est de l’interdire (sauf, éventuellement, depuis ta propre origine). Deux en-têtes (vus en Partie 2), à poser sur les pages sensibles (voire partout) :
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'X-Frame-Options: DENY(ouSAMEORIGIN) : l’ancien en-tête, largement supporté.frame-ancestors 'none'(ou'self') : la directive CSP moderne, plus flexible (permet de lister des origines autorisées si besoin). Elle prime surX-Frame-Optionslà où les deux sont présents.
Le réflexe : par défaut, ton appli n’a aucune raison d’être mise en iframe par un tiers → frame-ancestors 'none'. Si un cas légitime existe (widget embarqué), n’autoriser que les origines précises.
⚠️ Piège — Ne pas confondre clickjacking et CSRF : le clickjacking n’envoie pas de requête forgée, il fait cliquer la victime sur ta vraie interface. Un jeton anti-CSRF ne protège donc pas du clickjacking (le clic légitime porte le bon jeton !). La parade est spécifique : empêcher la mise en iframe (
frame-ancestors/X-Frame-Options). Les deux attaques sont complémentaires ; il faut les deux défenses.
Open redirect : la redirection détournée
Beaucoup d’applis ont une redirection paramétrable : après login, on renvoie l’utilisateur vers la page qu’il voulait (/login?redirect=/mon-espace) ; ou un « lien de sortie » (/go?url=...). Si la destination vient de l’utilisateur et n’est pas validée, c’est un open redirect : l’attaquant peut faire pointer la redirection vers n’importe quel site.
# Lien envoye a la victime (le domaine est CELUI DE CONFIANCE) :
https://app.formacampus.fr/go?url=https://evil.example/faux-login
# -> l'appli redirige la victime vers le site de phishingLe danger n’est pas une prise de contrôle directe, mais l’abus de confiance : le lien commence par ton domaine (que la victime reconnaît et à qui elle fait confiance), ce qui rend le phishing bien plus crédible. L’open redirect sert aussi à contourner des filtres (une allow-list de domaines qui autorise le tien, lequel rebondit ailleurs) et peut aggraver d’autres failles (vol de token OAuth via une redirect_uri laxiste — Partie 4).
La parade : valider la cible
- Rediriger uniquement vers des chemins internes : n’accepter que des destinations relatives à ton site (
/mon-espace), jamais une URL absolue fournie par l’utilisateur. C’est la solution la plus simple et la plus sûre. - Allow-list si des destinations externes sont vraiment nécessaires : n’autoriser qu’un ensemble fini de domaines connus.
- Rejeter les URL absolues et les schémas dangereux ; se méfier des contournements (
//evil.example— une URL « protocol-relative » qui ressemble à un chemin mais part ailleurs,/\evil.example, encodages).
// ✅ N'autoriser que des chemins internes (relatifs a l'origine)
function cibleSure(dest) {
// refuse les URL absolues, les "//host", les schemas
if (!dest || !dest.startsWith('/') || dest.startsWith('//')) return '/'
return dest
}
res.redirect(cibleSure(req.query.redirect))💡 Réflexe — Devant tout paramètre de redirection (
?redirect=,?url=,?next=,returnTo), demande-toi : « peut-il pointer ailleurs que chez moi ? ». Par défaut, n’autorise que des chemins internes (commençant par/mais pas//). C’est suffisant dans l’immense majorité des cas et ferme l’open redirect. Ne redirige jamais vers une URL absolue brute venant de l’utilisateur.
🎯 Côté attaquant — L’open redirect est un multiplicateur pour le phishing et le vol de jetons : un lien qui commence par le domaine légitime (
app.formacampus.fr) endort la méfiance, avant de rebondir vers une page piégée. L’attaquant l’utilise aussi pour contourner des protections qui « font confiance » aux redirections internes, ou pour détourner un flux OAuth. Pour le clickjacking, il cible les actions à un clic (valider, autoriser, supprimer) et compte sur l’absence deframe-ancestors. Les deux exploitent la confiance — dans ton domaine, dans ce que l’œil voit.
🧭 Sur FormaCampus — FormaCampus pose
X-Frame-Options: DENYetframe-ancestors 'none'sur toutes ses pages (aucun besoin d’être mis en iframe), fermant le clickjacking. Ses redirections post-login n’acceptent que des chemins internes (validés : commencent par/, pas//, pas de schéma), et le « lien sortant » vers des ressources partenaires passe par une allow-list de domaines. Le flux OAuth (chapitre 4.4) verrouille strictement sesredirect_uri. Résultat : impossible de faire deapp.formacampus.frun tremplin de phishing ou une interface piégée en iframe.
✏️ Exercices
Exercice 1 — CSRF ou clickjacking ? Une action sensible est protégée par un jeton anti-CSRF. Un attaquant charge quand même la page dans une iframe invisible et fait cliquer la victime sur le bouton d’action. L’attaque marche-t-elle ? Que faut-il ajouter ?
✅ Solution
Oui, l’attaque marche malgré le jeton anti-CSRF — c’est du clickjacking, pas du CSRF. Ici, c’est la victime elle-même qui clique sur le vrai bouton de ta vraie page (chargée en iframe avec sa session) : la requête envoyée est donc légitime et porte le bon jeton CSRF (généré par ta page). Le jeton ne protège pas, car il n’y a pas de requête « forgée » — juste un clic détourné. Il faut la parade spécifique au clickjacking : empêcher la mise en iframe par des tiers, avec X-Frame-Options: DENY et/ou CSP frame-ancestors 'none'. Les deux défenses (CSRF et anti-framing) sont nécessaires et complémentaires.
Exercice 2 — Ferme l’open redirect. Ce code redirige après connexion : res.redirect(req.query.next). Montre l’abus et corrige.
✅ Solution
Abus : next n’est pas validé → open redirect. Un attaquant diffuse https://app.formacampus.fr/login?next=https://evil.example/faux-login : le domaine de confiance rassure la victime, qui, après login, est redirigée vers le site de phishing. Variante : next=//evil.example (URL protocol-relative qui ressemble à un chemin interne mais part ailleurs). Correctif : n’autoriser que des chemins internes — vérifier que next commence par / mais pas par // (ni /\), rejeter toute URL absolue/schéma, et sinon rediriger vers une valeur par défaut sûre (/). Si des destinations externes sont indispensables, passer par une allow-list de domaines. Ne jamais rediriger vers une URL absolue brute fournie par l’utilisateur.
🧠 Quiz de révision
1. En quoi consiste le clickjacking ?
Charger ton site dans une iframe invisible superposée à un leurre, positionnée pour qu’un clic de la victime sur le leurre atteigne en réalité un bouton sensible de ton interface (valider, autoriser, supprimer). C’est un abus de confiance visuel : on trompe l’œil, on ne « pirate » rien techniquement.
2. Comment empêche-t-on le clickjacking ?
En interdisant la mise en iframe de ton site par des tiers : X-Frame-Options: DENY (ou SAMEORIGIN) et/ou la directive CSP frame-ancestors 'none' (ou 'self'). Par défaut, une appli n’a aucune raison d’être encadrée par un tiers.
3. Pourquoi un jeton anti-CSRF ne protège-t-il pas du clickjacking ?
Parce que le clickjacking ne forge pas de requête : c’est la victime qui clique sur ta vraie page (en iframe, avec sa session). La requête est donc légitime et porte le bon jeton CSRF. La parade est spécifique : empêcher l’encadrement (frame-ancestors).
4. Qu’est-ce qu’un open redirect et pourquoi est-il dangereux ?
Une redirection dont la destination vient de l’utilisateur sans validation (/go?url=...), permettant de pointer vers n’importe quel site. Danger : un lien commençant par ton domaine de confiance rebondit vers du phishing (crédibilité accrue), contourne des filtres, ou aggrave un flux OAuth.
5. Quelle est la parade la plus simple à l’open redirect ?
N’autoriser que des chemins internes : la destination doit commencer par / mais pas // (ni schéma, ni URL absolue). Si des cibles externes sont nécessaires, utiliser une allow-list de domaines. Jamais de redirection vers une URL absolue brute fournie par l’utilisateur.
Chapitre suivant : CORS bien configuré — comprendre CORS (un assouplissement de la SOP, pas une protection) et le configurer sans se percer un trou.