Lâattaque par âclickjackingâ permet à une page malveillante de cliquer sur un âsite victimeâ au nom du visiteur.
De nombreux sites ont été piratés de cette manière, notamment Twitter, Facebook, Paypal et dâautres sites. Ils ont tous été réparés, bien sûr.
Lâidée
Lâidée est très simple.
Voici comment le clickjacking a été fait avec Facebook:
- Un visiteur est attiré vers la page malveillante. Peu importe comment.
- La page contient un lien inoffensif (du type âenrichissez-vous maintenantâ ou âcliquez ici, très drôleâ).
- Au-dessus de ce lien, la page maléfique place un
<iframe>transparent avecsrcde facebook.com, de telle sorte que le bouton âJâaimeâ se trouve juste au-dessus de ce lien. Habituellement, cela se fait avecz-index. - En essayant de cliquer sur le lien, le visiteur clique en fait sur le bouton.
La démo
Voici à quoi ressemble la page malveillante. Pour que les choses soient claires, le <iframe> est semi-transparent (dans les vraies pages maléfiques, il est totalement transparent):
<style>
iframe { /* iframe du site de la victime */
width: 400px;
height: 100px;
position: absolute;
top:0; left:-20px;
opacity: 0.5; /* en réalité opacity:0 */
z-index: 1;
}
</style>
<div>Click to get rich now:</div>
<!-- L'url du site de la victime -->
<iframe src="/clickjacking/facebook.html"></iframe>
<button>Click here!</button>
<div>...And you're cool (I'm a cool hacker actually)!</div>
La démo complète de lâattaque:
<!DOCTYPE HTML>
<html>
<body style="margin:10px;padding:10px">
<input type="button" onclick="alert('Like pressed on facebook.html!')" value="I LIKE IT !">
</body>
</html><!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<style>
iframe {
width: 400px;
height: 100px;
position: absolute;
top: 5px;
left: -14px;
opacity: 0.5;
z-index: 1;
}
</style>
<div>Click to get rich now:</div>
<!-- The url from the victim site -->
<iframe src="facebook.html"></iframe>
<button>Click here!</button>
<div>...And you're cool (I'm a cool hacker actually)!</div>
</body>
</html>Ici, nous avons un <iframe src="facebook.html"> semi-transparent, et dans lâexemple, nous pouvons le voir planer au-dessus du bouton. En cliquant sur le bouton, on clique en fait sur lâiframe, mais lâutilisateur ne le voit pas, car lâiframe est transparent.
Par conséquent, si le visiteur est connecté sur Facebook (lâoption âSe souvenir de moiâ est généralement activée), un bouton âJâaimeâ est ajouté. Sur Twitter, ce serait un bouton âSuivreâ.
Voici le même exemple, mais plus proche de la réalité, avec opacity:0 pour <iframe>:
<!DOCTYPE HTML>
<html>
<body style="margin:10px;padding:10px">
<input type="button" onclick="alert('Like pressed on facebook.html!')" value="I LIKE IT !">
</body>
</html><!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<style>
iframe {
width: 400px;
height: 100px;
position: absolute;
top: 5px;
left: -14px;
opacity: 0;
z-index: 1;
}
</style>
<div>Click to get rich now:</div>
<!-- The url from the victim site -->
<iframe src="facebook.html"></iframe>
<button>Click here!</button>
<div>...And you're cool (I'm a cool hacker actually)!</div>
</body>
</html>Tout ce dont nous avons besoin pour attaquer â câest de positionner le <iframe> sur la page malveillante de manière à ce que le bouton soit juste au-dessus du lien. Ainsi, lorsquâun utilisateur clique sur le lien, il clique en fait sur le bouton. Câest généralement faisable avec CSS.
Lâattaque nâaffecte que les actions de la souris (ou des actions similaires, comme les tapotements sur les mobiles).
La saisie au clavier est très difficile à rediriger. Techniquement, si nous avons un champ de texte à pirater, nous pouvons alors positionner un iframe de telle sorte que les champs de texte se chevauchent. Ainsi, lorsquâun visiteur essaie de se concentrer sur la saisie quâil voit sur la page, il se concentre en fait sur la saisie à lâintérieur de lâiframe.
Mais il y a un problème. Tout ce que le visiteur tape sera caché, car lâiframe nâest pas visible.
Les gens sâarrêtent généralement de taper lorsquâils ne voient pas leurs nouveaux caractères sâimprimer à lâécran.
Défenses de la vieille école (faibles)
La défense la plus ancienne est un bout de JavaScript qui interdit lâouverture de la page dans un iframe (ce quâon appelle le âframebustingâ).
Cela ressemble à ceci:
if (top != window) {
top.location = window.location;
}
Câest-à -dire que si la fenêtre découvre quâelle nâest pas en haut, elle se met automatiquement en haut.
Ce nâest pas une défense fiable, car il existe de nombreuses façons de la contourner. En voici quelques-unes.
Blocage de la navigation supérieure
Nous pouvons bloquer la transition provoquée par le changement de top.location dans lâévénement beforeunload.
La page du haut (appartenant au pirate) lui attribue un événement de prévention, comme ceci:
window.onbeforeunload = function() {
return false;
};
Lorsque le iframe essaie de changer top.location, le visiteur reçoit un message lui demandant sâil veut quitter le site.
Dans la plupart des cas, le visiteur répondra négativement parce quâil ne connaît pas lâiframe â tout ce quâil peut voir est la page dâaccueil, il nâa aucune raison de la quitter. Donc top.location ne changera pas!
En action:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div>Changes top.location to javascript.info</div>
<script>
top.location = 'https://javascript.info';
</script>
</body>
</html><!doctype html>
<html>
<head>
<meta charset="UTF-8">
<style>
iframe {
width: 400px;
height: 100px;
position: absolute;
top: 0;
left: -20px;
opacity: 0;
z-index: 1;
}
</style>
<script>
function attack() {
window.onbeforeunload = function() {
window.onbeforeunload = null;
return "Want to leave without learning all the secrets (he-he)?";
};
document.body.insertAdjacentHTML('beforeend', '<iframe src="iframe.html">');
}
</script>
</head>
<body>
<p>After a click on the button the visitor gets a "strange" question about whether they want to leave.</p>
<p>Probably they would respond "No", and the iframe protection is hacked.</p>
<button onclick="attack()">Add a "protected" iframe</button>
</body>
</html>Lâattribut Sandbox
Lâune des choses limitées par lâattribut sandbox est la navigation. Une iframe protégée par un sandbox ne peut pas modifier lâattribut top.location.
Nous pouvons donc ajouter lâiframe avec sandbox="allow-scripts allow-forms". Cela assouplirait les restrictions, autorisant les scripts et les formulaires. Mais nous omettons allow-top-navigation pour que la modification de top.location soit interdite.
Voici le code:
<iframe sandbox="allow-scripts allow-forms" src="facebook.html"></iframe>
Il existe également dâautres moyens de contourner cette simple protection.
X-Frame-Options
Lâen-tête X-Frame-Options côté serveur peut autoriser ou interdire lâaffichage de la page dans un iframe.
Il doit être envoyé exactement comme un en-tête HTTP : le navigateur lâignorera sâil se trouve dans la balise HTML <meta>. Ainsi, <meta http-equiv="X-Frame-Options"...> ne fera rien.
Lâen-tête peut avoir 3 valeurs:
DENY- Ne jamais afficher la page à lâintérieur dâun iframe.
SAMEORIGIN- Autoriser à lâintérieur dâun iframe si le document parent a la même origineâ¦
ALLOW-FROM domain- Autoriser à lâintérieur dâun iframe si le document parent appartient au domaine donnéâ¦
Par exemple, Twitter utilise X-Frame-Options: SAMEORIGIN.
Voici le résultat:
<iframe src="https://twitter.com"></iframe>
Selon votre navigateur, le iframe ci-dessus est soit vide, soit vous avertit que le navigateur ne permet pas de naviguer sur cette page de cette manière.
Affichage de la fonctionnalité désactivée
Lâen-tête X-Frame-Options a un effet secondaire. Les autres sites ne pourront pas afficher notre page dans un iframe, même sâils ont de bonnes raisons de le faire.
Il existe donc dâautres solutions⦠Par exemple, on peut âcouvrirâ la page avec une <div> avec les styles height : 100% ; width : 100%;, pour quâelle intercepte tous les clics. Cette <div> doit être retirée si window == top ou si lâon sâaperçoit que lâon nâa pas besoin de cette protection.
Quelque chose comme ça:
<style>
#protector {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 99999999;
}
</style>
<div id="protector">
<a href="/" target="_blank">Go to the site</a>
</div>
<script>
// il y aura une erreur si la fenêtre supérieure est d'une origine différente
// mais c'est bon ici
if (top.document.domain == document.domain) {
protector.remove();
}
</script>
La démo:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<style>
#protector {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 99999999;
}
</style>
</head>
<body>
<div id="protector">
<a href="/" target="_blank">Go to the site</a>
</div>
<script>
if (top.document.domain == document.domain) {
protector.remove();
}
</script>
This text is always visible.
But if the page was open inside a document from another domain, the div over it would prevent any actions.
<button onclick="alert(1)">Click wouldn't work in that case</button>
</body>
</html><!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<iframe src="iframe.html"></iframe>
</body>
</html>Attribut de cookie âsamesiteâ
Lâattribut de cookie samesite peut également prévenir les attaques de clickjacking.
Un cookie avec un tel attribut nâest envoyé à un site web que sâil est ouvert directement, et non par lâintermédiaire dâun iframe ou autre. Plus dâinformations dans le chapitre Cookies, document.cookie.
Si le site, tel que Facebook, avait lâattribut samesite dans son cookie dâauthentification, comme ceci:
Set-Cookie: authorization=secret; samesite
â¦Ce cookie ne serait donc pas envoyé lorsque Facebook est ouvert dans une iframe depuis un autre site. Donc lâattaque échoue.
Lâattribut de cookie samesite nâaura pas dâeffet lorsque les cookies ne sont pas utilisés. Cela peut permettre à dâautres sites Web dâafficher facilement nos pages publiques, non authentifiées, dans des iframes.
Toutefois, cela peut également permettre aux attaques par détournement de clics de fonctionner dans quelques cas limités. Un site Web de vote anonyme qui empêche les doubles votes en vérifiant les adresses IP, par exemple, serait toujours vulnérable au détournement de clics parce quâil nâauthentifie pas les utilisateurs à lâaide de cookies.
Résumé
Le détournement de clics est un moyen de âtromperâ les utilisateurs en les incitant à cliquer sur un site victime sans même savoir ce qui se passe. Câest dangereux sâil y a des actions importantes activées par le clic.
Un hacker peut poster un lien vers sa page malveillante dans un message, ou attirer les visiteurs sur sa page par dâautres moyens. Il existe de nombreuses variantes.
Dâun certain point de vue, lâattaque nâest âpas profondeâ : le pirate ne fait quâintercepter un seul clic. Mais dâun autre point de vue, si le pirate sait quâaprès le clic, un autre contrôle apparaîtra, il peut utiliser des messages astucieux pour contraindre lâutilisateur à cliquer également sur ces contrôles.
Cette attaque est assez dangereuse, car lorsque nous concevons lâinterface utilisateur, nous ne prévoyons généralement pas quâun pirate puisse cliquer au nom du visiteur. Les vulnérabilités peuvent donc se trouver dans des endroits totalement inattendus.
- Il est recommandé dâutiliser
X-Frame-Options : SAMEORIGINsur les pages (ou les sites entiers) qui ne sont pas destinées à être affichées dans des iframes. - Utilisez une couverture
<div>si nous voulons permettre à nos pages dâêtre affichées dans des iframes, tout en restant sécurisées.
Commentaires
<code>, pour plusieurs lignes â enveloppez-les avec la balise<pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepenâ¦)