âClickjackingâ je útok, který umožÅuje zlé stránce, aby kliknula na âstránku obÄtiâ jménem návÅ¡tÄvnÃka.
TÃmto způsobem již bylo napadeno mnoho stránek, mezi nimi Twitter, Facebook, Paypal a jiné. VÅ¡echny už byly samozÅejmÄ opraveny.
Myšlenka
Myšlenka je velmi jednoduchá.
Clickjacking na Facebook byl proveden tÃmto způsobem:
- NávÅ¡tÄvnÃk je nalákán na zlou stránku. Nezáležà na tom, jak.
- Stránka obsahuje neÅ¡kodnÄ vypadajÃcà odkaz (napÅ. âzbohatnÄte hned teÄâ nebo âkliknÄte sem, obrovská legraceâ).
- PÅes tento odkaz zlá stránka umÃstà průhledný
<iframe>sesrcz facebook.com takovým způsobem, že pÅÃmo na tomto odkazu je tlaÄÃtko âTo se mi lÃbÃâ. To je obvykle provedeno použitÃmz-index. - Když se návÅ¡tÄvnÃk pokusà kliknout na tento odkaz, ve skuteÄnosti klikne na tlaÄÃtko.
Demo
Zlá stránka vypadá následovnÄ. Aby to bylo vidÄt, je <iframe> poloprůhledný (na skuteÄných zlých stránkách je zcela průhledný):
<style>
iframe { /* iframe ze stránky obÄti */
width: 400px;
height: 100px;
position: absolute;
top:0; left:-20px;
opacity: 0.5; /* v realitÄ opacity:0 */
z-index: 1;
}
</style>
<div>KliknÄte pro okamžité zbohatnutÃ:</div>
<!-- URL ze stránky obÄti -->
<iframe src="/clickjacking/facebook.html"></iframe>
<button>KliknÄte sem!</button>
<div>...A jste cool (ve skuteÄnosti jsem já cool hacker)!</div>
Celé demo útoku:
<!DOCTYPE HTML>
<html>
<body style="margin:10px;padding:10px">
<input type="button" onclick="alert('Jako stisknout na facebook.html!')" value="TO SE MI LÃBÃ !">
</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>KliknÄte pro okamžité zbohatnutÃ:</div>
<!-- URL ze stránky obÄti -->
<iframe src="facebook.html"></iframe>
<button>KliknÄte sem!</button>
<div>...A jste cool (ve skuteÄnosti jsem já cool hacker)!</div>
</body>
</html>Zde máme poloprůhledný <iframe src="facebook.html"> a v pÅÃkladu vidÃme, jak se vznášà nad tlaÄÃtkem. KliknutÃm na tlaÄÃtko uživatel ve skuteÄnosti klikne na vnitÅnà rám, ale to nevidÃ, protože vnitÅnà rám je průhledný.
Důsledkem je, že pokud je návÅ¡tÄvnÃk pÅihlášen na Facebook (âPamatuj si mÄâ je obvykle zapnuto), pak pÅidá âTo se mi lÃbÃâ. Na Twitteru by to bylo tlaÄÃtko âFollowâ.
Následuje stejný pÅÃklad, ale bližšà realitÄ, pro <iframe> má opacity:0:
<!DOCTYPE HTML>
<html>
<body style="margin:10px;padding:10px">
<input type="button" onclick="alert('Jako stisknout na facebook.html!')" value="TO SE MI LÃBÃ !">
</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>KliknÄte pro okamžité zbohatnutÃ:</div>
<!-- URL ze stránky obÄti -->
<iframe src="facebook.html"></iframe>
<button>KliknÄte sem!</button>
<div>...A jste cool (ve skuteÄnosti jsem já cool hacker)!</div>
</body>
</html>VÅ¡echno, co k útoku potÅebujeme, je umÃstit <iframe> na zlou stránku tak, aby tlaÄÃtko bylo umÃstÄno pÅesnÄ pÅes odkaz. Když tedy uživatel klikne na odkaz, klikne ve skuteÄnosti na tlaÄÃtko. To lze obvykle provést pomocà CSS.
Ãtok má vliv jen na akce myÅ¡i (nebo podobné, napÅ. doteky na mobilu).
PÅesmÄrovat vstup z klávesnice je mnohem obtÞnÄjÅ¡Ã. Technicky jestliže chceme nabourat textové pole, můžeme umÃstit vnitÅnà rám tak, aby se textová pole navzájem pÅekrývala. Když se tedy návÅ¡tÄvnÃk pokusà vstoupit na textové pole, které vidà na stránce, vstoupà ve skuteÄnosti na pole uvnitÅ rámu.
Pak ale nastává problém. VÅ¡echno, co návÅ¡tÄvnÃk napÃÅ¡e, bude ukryté, protože rám nenà vidÄt.
Když lidé neuvidà své napsané znaky na obrazovce, obvykle pÅestanou psát.
Obrany ze staré školy (slabé)
Nejstaršà obranou je krátký kód v JavaScriptu, který zakáže otevÅenà stránky v rámu (tzv. âframebustingâ).
Vypadá následovnÄ:
if (top != window) {
top.location = window.location;
}
To znamená: jestliže okno zjistÃ, že nenà vrchnÃ, automaticky se nastavà jako vrchnÃ.
Tato obrana nenà spolehlivá, protože existuje mnoho způsobů, jak ji obejÃt. PodÃvejme se na nÄkteré z nich.
Blokovánà navigace ve vrchnÃm oknÄ
Můžeme zablokovat pÅesun způsobený zmÄnou top.location v handleru události beforeunload.
Vrchnà stránka (uzavÃrajÃcÃ, patÅÃcà hackerovi) nastavà handler, který tomu zabránÃ, napÅÃklad:
window.onbeforeunload = function() {
return false;
};
Když se iframe pokusà zmÄnit top.location, návÅ¡tÄvnÃk dostane otázku, zda opravdu chce odejÃt.
Ve vÄtÅ¡inÄ pÅÃpadů návÅ¡tÄvnÃk odpovà zápornÄ, neboÅ¥ o vnitÅnÃm rámu nevà â nevidà nic jiného než vrchnà stránku, a tak nemá důvod odejÃt. Proto se top.location nezmÄnÃ!
V akci:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div>ZmÄnà top.location na 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 útok() {
window.onbeforeunload = function() {
window.onbeforeunload = null;
return "Chcete odejÃt, aniž se dozvÃte vÅ¡echna ta tajemstvà (haha)?";
};
document.body.insertAdjacentHTML('beforeend', '<iframe src="iframe.html">');
}
</script>
</head>
<body>
<p>Po kliknutà na tlaÄÃtko návÅ¡tÄvnÃk dostane âpodivnouâ otázku, zda chce odejÃt.</p>
<p>Odpovà pravdÄpodobnÄ âNeâ a ochrana vnitÅnÃho rámu bude prolomena.</p>
<button onclick="útok()">PÅidá âchránÄnýâ vnitÅnà rám</button>
</body>
</html>Atribut sandbox
Jednou z vÄcÃ, které atribut sandbox omezuje, je navigace. VnitÅnà rám obsahujÃcà sandbox nemůže zmÄnit top.location.
Můžeme tedy pÅidat vnitÅnà rám obsahujÃcà sandbox="allow-scripts allow-forms". TÃm se omezenà zmÃrnà a budou povoleny skripty a formuláÅe. Neuvedeme vÅ¡ak allow-top-navigation, takže zmÄna top.location bude zakázána.
Kód je následujÃcÃ:
<iframe sandbox="allow-scripts allow-forms" src="facebook.html"></iframe>
Existujà i jiné způsoby, jak tuto jednoduchou ochranu pÅekonat.
X-Frame-Options
HlaviÄka X-Frame-Options ze strany serveru může povolit nebo zakázat zobrazenà stránky v rámu.
Musà být poslána jako skuteÄná HTTP hlaviÄka: pokud ji prohlÞeÄ nalezne v HTML znaÄce <meta>, bude ji ignorovat, takže <meta http-equiv="X-Frame-Options"...> nic neudÄlá.
HlaviÄka může mÃt tÅi hodnoty:
DENY- Vůbec nikdy nelze zobrazit tuto stránku v rámu.
SAMEORIGIN- Umožnà zobrazenà v rámu, jestliže rodiÄovský dokument pocházà ze stejného původu.
ALLOW-FROM doména- Umožnà zobrazenà v rámu, jestliže rodiÄovský dokument pocházà z uvedené domény.
NapÅÃklad Twitter použÃvá X-Frame-Options: SAMEORIGIN.
Výsledek je následujÃcÃ:
<iframe src="https://twitter.com"></iframe>
V závislosti na vaÅ¡em prohlÞeÄi bude uvedený iframe buÄ prázdný, nebo vám oznámÃ, že prohlÞeÄ nedovolil navigovat na stránku tÃmto způsobem.
Zobrazenà s potlaÄenou funkcionalitou
HlaviÄka X-Frame-Options má vedlejšà efekt. Jiné stránky nebudou moci zobrazit naÅ¡i stránku v rámu, ani když pro to majà dobrý důvod.
Existujà tedy i jiná ÅeÅ¡enÃ⦠Můžeme napÅÃklad stránku âzakrýtâ znaÄkou <div> se styly height: 100%; width: 100%; tak, aby zachytila vÅ¡echna kliknutÃ. Tato <div> bude odstranÄna, pokud window == top nebo pokud zjistÃme, že tuto ochranu nepotÅebujeme.
NÄco takového:
<style>
#ochránce {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 99999999;
}
</style>
<div id="ochránce">
<a href="/" target="_blank">PÅejdÄte na stránku</a>
</div>
<script>
// pokud je vrchnà okno z jiného původu, nastane chyba
// ale to je tady v poÅádku
if (top.document.domain == document.domain) {
ochránce.remove();
}
</script>
Ukázka:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<style>
#ochránce {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 99999999;
}
</style>
</head>
<body>
<div id="ochránce">
<a href="/" target="_blank">PÅejdÄte na stránku</a>
</div>
<script>
if (top.document.domain == document.domain) {
ochránce.remove();
}
</script>
Tento text je vždy viditelný.
Pokud vÅ¡ak byla stránka otevÅena uvnitÅ dokumentu z jiné domény, div nad nà zabránà vÅ¡em akcÃm.
<button onclick="alert(1)">Kliknutà v tom pÅÃpadÄ nebude fungovat</button>
</body>
</html><!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<iframe src="iframe.html"></iframe>
</body>
</html>Cookie atribut samesite
Clickjackingům může pÅedejÃt i cookie atribut samesite.
Cookie s takovým atributem je poslán na webovou stránku jen tehdy, když je otevÅena pÅÃmo, ne v rámu nebo jinak. VÃce informacà najdete v kapitole Cookies, document.cookie.
Jestliže nÄjaká stránka, napÅ. Facebook, má ve své autentifikaÄnà cookie atribut samesite, napÅÃklad takto:
Set-Cookie: authorization=secret; samesite
â¦Pak taková cookie nebude poslána, když je Facebook otevÅen ve vnitÅnÃm rámu z jiné stránky. Ãtok tedy neuspÄje.
Atribut samesite nebude mÃt žádný efekt, když nebudou použÃvány cookies. To může jiným stránkám umožnit snadno zobrazit naÅ¡e veÅejné, neautentifikované stránky ve vnitÅnÃch rámech.
NicménÄ to může také v nÄkterých pÅÃpadech umožnit, aby clickjacking fungoval. NapÅÃklad anonymnà hlasovacà stránka, která bránà dvojÃmu hlasovánà ovÄÅenÃm IP adresy, bude clickjackingem stále zranitelná, protože neautentifikuje uživatele pomocà cookies.
ShrnutÃ
Clickjacking je způsob, jak âpÅimÄtâ uživatele kliknout na stránku obÄti, aniž by vůbec vÄdÄl, co se dÄje. Pokud tam jsou důležité akce aktivované kliknutÃm, je to nebezpeÄné.
Hacker může umÃstit odkaz na svou zlou stránku do zprávy nebo nalákat návÅ¡tÄvnÃky na svou stránku jinými způsoby. Možnostà je mnoho.
Z jednoho pohledu nenà tento útok âhlubokýâ: vÅ¡echno, co hacker udÄlá, je zachycenà jediného kliknutÃ. AvÅ¡ak z jiného pohledu, jestliže hacker vÃ, že po kliknutà se objevà dalšà ovládacà prvky, může lstivými zprávami nalákat uživatele, aby kliknul i na nÄ.
Tento útok je pomÄrnÄ nebezpeÄný, neboÅ¥ když navrhujeme uživatelské rozhranÃ, obvykle nepÅedpokládáme, že na nÄ může kliknout hacker jménem uživatele. Zranitelnosti tedy lze najÃt na zcela neÄekaných mÃstech.
- DoporuÄuje se použÃvat na stránkách (nebo celých webových sÃdlech), které nejsou urÄeny k zobrazenà v rámech,
X-Frame-Options: SAMEORIGIN. - Pokud chcete, aby se vaÅ¡e stránky mohly zobrazovat ve vnitÅnÃch rámech, ale stále zůstaly bezpeÄné, použijte pÅekryvný
<div>.
KomentáÅe
<code>, pro nÄkolik Åádků je obalte znaÄkou<pre>, pro vÃce než 10 Åádků vložte odkaz na pÃskoviÅ¡tÄ (plnkr, jsbin, codepenâ¦)