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âŠ)