حملهی “clickjacking” به یک صفحهی شیطانی اجازه میدهد تا روی “سایت قربانی” از طرف بازدیدکننده کلیک کند.
تعداد زیادی از سایتها با این راه هک میشوند، شامل Twitter، Facebook، Paypal و سایتهای دیگر. البته همهی آنها درست شدهاند.
ایده
ایده بسیار ساده است.
اینجا میگوییم که clickjacking چگونه با Facebook انجام شد:
- یک بازدیدکننده به صفحهی شیطانی اغوا میشود. مهم نیست چگونه.
- این صفحه درون خود یک لینک به ظاهر غیرآسیبزننده دارد (مثال “الان ثروتمند شوید” یا “اینجا کلیک کنید، بسیار بامزه است”).
- روی آن لینک، آن صفحهی شیطانی یک
<iframe>
شفاف باsrc
از facebook.com قرار میدهد، به طوری که دکمهی “پسندیدن” درست بالای لینک است. معمولا این کار باz-index
انجام میشود. - در تلاش برای کلیک کردن لینک، بازدیدکننده در واقع روی دکمه کلیک میکند.
نسخهی نمایشی
اینجا میبینیم که صفحهی شیطانی چگونه به نظر میرسد. برای واضح شدن همه چیز، <iframe>
نیمهشفاف است (در صفحات شیطانی واقعی، کاملا شفاف است):
<style>
iframe { /* از سایت قربانی iframe */
width: 400px;
height: 100px;
position: absolute;
top:0; left:-20px;
opacity: 0.5; /* opacity:0 در واقع */
z-index: 1;
}
</style>
<div>کلیک کنید تا الان ثروتمند شوید:</div>
<!-- لینک سایت قربانی -->
<iframe src="/clickjacking/facebook.html"></iframe>
<button>!اینجا کلیک کنید</button>
<div>!و شما باحال هستید(در واقع من یک هکر باحال هستم)...</div>
نسخهی نمایشی کامل این حمله:
<!DOCTYPE HTML>
<html>
<body style="margin:10px;padding:10px">
<input type="button" onclick="alert('فشار داده شد 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>:کلیک کنید تا الان ثروتمند شوید</div>
<!-- لینک سایت قربانی -->
<iframe src="facebook.html"></iframe>
<button>اینجا کلیک کنید!</button>
<div>...و تو باحال هستی (در واقع من یک هکر باحال هستم)</div>
</body>
</html>
اینجا ما یک <iframe src="facebook.html">
نیمهشفاف داریم، و در مثال میتوانیم ببینیم که روی دکمه شناور است. یک کلیک روی دکمه در واقع روی iframe کلیک میکند، اما این برای کاربر قابل دیدن نیست، چون iframe شفاف است.
در نتیجه،اگر بازدیدکننده در Facebook مجاز باشد (معمولا “مرا به خاطر بسپار” روشن است)، آنگاه یک “پسندیدم” اضافه میکند. در Twitter، این یک دکمهی “دنبال کردن” خواهد بود.
اینجا مثال نمونه را داریم، اما نزدیکتر به واقعیت، با opacity:0
برای <iframe>
:
<!DOCTYPE HTML>
<html>
<body style="margin:10px;padding:10px">
<input type="button" onclick="alert('!فشار داده شد 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>کلیک کنید تا الان ثروتمند شوید:</div>
<!-- لینک سایت قربانی -->
<iframe src="facebook.html"></iframe>
<button>Click here!</button>
<div>...و تو باحال هستی (در واقع من یک هکر باحال هستم)!</div>
</body>
</html>
تمام چیزی که برای حمله نیاز داریم – این است که <iframe>
را در صفحهی شیطانی به گونهای قرار دهیم که دکمه درست روی لینک باشد. بنابراین، وقتی یک کاربر روی لینک کلیک میکند، در واقع روی دکمه کلیک میکند. این معمولا با CSS قابل انجام است.
این حمله فقط فعالیتهای با موش را تحت تاثیر قرار میدهد (یا مشابه، مثل ضربهها روی تلفن همراه).
.فوکوس میکند iframe داخل input که در صفحه میبیند فوکوس کند، در واقع روی input را به گونهای قرار دهیم که فیلدهای متنی روی یکدیگر همپوشانی داشته باشند.بنابراین وقتی یک بازدیدکننده تلاش میکند که روی یک iframe تغییر مسیر ورودی صفحهکلید بسیار دشوار است. از نظر فنی، اگر یک فیلد متنی برای هک داشته باشیم، میتوانیم یک
.قابل دیدن نیست iframe ،اما آنگاه یک مشکل وجود دارد. هرچیزی که یک بازدیدکننده تایپ میکند پنهان میشود چون
مردم معمولا وقتی نمیتوانند کاراکترهای جدید را در حال چاپ شدن روی صفحهنمایش ببینند تایپ کردن را متوقف میکنند.
دفاع قدیمی (ضعیف)
قدیمیترین دفاع کمی JavaScript است که باز کردن یک صفحه در یک فریم را ممنوع میکند (به اصطلاح “framebusting”)
آن شبیه این است:
if (top != window) {
top.location = window.location;
}
یعنی: اگر پنجره متوجه شود که در بالا نیست، به طور خودکار خود را در بالای صفحه قرار میدهد.
این یک دفاع قابل اعتماد نیست، چون راههای زیادی وجود دارد که هک آن وجود دارد. بیایید چند مورد را پوشش دهیم.
مسدود کردن top-navigation
میتوانیم transition ناشی از تغییر top.location
را در event handler beforeunload مسدود کنیم.
صفحهی بالا (که یک مورد را محصور میکند، متعلق به هکر است) یک preventing handler را برای آن تنظیم میکند، مانند این:
window.onbeforeunload = function() {
return false;
};
وقتی iframe
تلاش میکند top.location
را تغییر دهد، بازدیدکننده پیامی میگیرد که میپرسد میخواهند ترک کنند یا نه.
در بیشتر موارد، بازدیدکننده پاسخ منفی میدهد، زیرا از iframe اطلاعی ندارد – تنها چیزی که میتوانند ببینند صفحه بالایی است، دلیلی برای ترک آن وجود ندارد. بنابراین top.location
تغییری نخواهد کرد.
در عمل:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div> تغییر میدهد javascript.info را به top.location </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 "میخواهی بدون اینکه تمام رازها را یادبگیری بروی؟ (ها-ها)";
};
document.body.insertAdjacentHTML('beforeend', '<iframe src="iframe.html">');
}
</script>
</head>
<body>
<p>بعد از یک کلیک روی دکمه، بازدیدکننده یک سوال "عجیب" دریافت میکند دبارهی اینکه میخواهد ترک کند یا نه.</p>
<p>احتمالا آنها پاسخ میدهد "نه" و حفاظت آیفریم هک میشود.</p>
<button onclick="attack()">یک آیفریم "حفاظتشده" اضافه کنید.</button>
</body>
</html>
Sandbox attribute
یکی از چیزهایی که توسط sandbox
attribute محدود میشود navigation است. یک sandboxed iframe ممکن است top.location
را تغییر ندهد.
پس ما میتوانیم iframe را با sandbox="allow-scripts allow-forms"
اضافه کنیم. این امر محدودیتها را کاهش میدهد و اسکریپتها و فرمها را مجاز میکند. اما ما allow-top-navigation
را حذف می کنیم تا تغییر top.location
ممنوع باشد.
کد اینجاست:
<iframe sandbox="allow-scripts allow-forms" src="facebook.html"></iframe>
راههای دیگری نیز برای دور زدن این حفاظت ساده وجود دارد.
X-Frame-Options
هدر سمت سرور X-Frame-Options
میتواند نمایش صفحه را در یک فریم مجاز یا ممنوع کند.
این باید دقیقا به عنوان HTTP-header فرستاده شود: اگر مرورگر آن را در HTML <meta>
tag پیدا کند، آن را نادیده میگیرد. بنابراین <meta http-equiv="X-Frame-Options"...>
هیچ کاری انجام نخواهد داد.
هدر مینواند 3 مقدار داشته باشد:
DENY
- هیچگاه صفحه را درون یک فریم نشان ندهید.
SAMEORIGIN
- اگر parent document از منبع یکسان بیاید، درون یک فریم را مجاز میکند.
ALLOW-FROM domain
- گر parent document از دامنهی داده شده باشد، درون یک فریم را مجاز میکند.
برای مثال، Twitter از X-Frame-Options: SAMEORIGIN
استفاده میکند.
نتیجه اینجاست:
<iframe src="https://twitter.com"></iframe>
بسته به مرورگرتان، iframe
بالا یا خالی است یا به شما هشدار میدهد که مرورگر اجازه نمیدهد آن صفحه به این روش پیمایش کند.
نمایش با عملکرد غیرفعال
هدر X-Frame-Options
یک عارضهی جانبی دارد. بقیهی سایتها قادر نخواهند بود که صفحهی ما را در یک فریم نمایش دهند، حتی اگر دلایل خوبی داشته باشند که انجام دهند.
بنابراین، راهحلهای دیگری وجود دارد… برای مثال، میتوانیم یک صفحه را با یک <div>
با استایلهای height: 100%; width: 100%;
“پوشش” دهیم، در نتیجه تمام کلیکها را قطع میکند. اگر window == top
یا متوجه شویم که به حفاظت نیازی نداریم، <div>
باید حذف شود.
چیزی شبیه به این:
<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>
// اگر پنجرهی بالایی از منبع متفاوتی باشد، خطا خواهیم داشت
// اما اینجا اوکی است
if (top.document.domain == document.domain) {
protector.remove();
}
</script>
نسخهی پیشنمایش:
<!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">به سایت بروید.</a>
</div>
<script>
if (top.document.domain == document.domain) {
protector.remove();
}
</script>
.این متن همیشه قابل دیدن است
.روی آن از هر فعالیتی جلوگیری میکرد div از یک دامنهی دیگر باز بود document اما اگر صفحه درون یک
<button onclick="alert(1)">در آن صورت کلیک کار نمیکرد.</button>
</body>
</html>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<iframe src="iframe.html"></iframe>
</body>
</html>
Samesite cookie attribute
برای جلوگیری از حملههای clickjacking، میتوان از samesite
cookie attribute نیز استفاده کرد.
یک کوکی با این attribute فقط اگر به صورت مستقیم باز شده باشد، نه از طریق یک فریم، یا هر چیز دیگری، برای یک وبسایت فرستاده میشود. اطلاعات بیشتر در بخش Cookies, document.cookie.
اگر سایت، مثل Facebook، روی authentication cookie خود samesite
attribute را داشت، مثل این:
Set-Cookie: authorization=secret; samesite
…آنگاه همچین کوکی وقتی Facebook در iframe از یک سایت دیگر باز میشود، فرستاده نمیشود. بنابراین حمله شکست میخورد.
وقتی کوکیها استفاده نمیشوند، samesite
cookie attribute تاثیری نخواهد داشت. این میتواند بع وبسایتها اجازه دهد که به سادگی صفحات عمومی و احراز هویت نشدهی ما را در iframeها نمایش دهند.
با این حال، این ممکن است به حملات clickjacking در چند مورد محدود نیز اجازه دهد. برای مثال، یک وبسایت نظرسنجی ناشناس که با بررسی آدرسهای IP از رایگیری تکراری جلوگیری میکند، همچنان در برابر clickjacking آسیبپذیر است زیرا کاربران را با استفاده از کوکیها احراز هویت نمیکند.
خلاصه
یک راه برای “فریب دادن” کاربران که روی سایت قربانی کلیک کنند بدون اینکه حتی بدانند چه اتفاقی میافتد، Clickjacking است. اگر اقدامات مهم فعال شده با کلیک وجود داشته باشد، این خطرناک است.
یک هکر میتواند لینکی به صفحهی شیطانی خود در یک پیام ارسال کند یا بازدیدکنندگان را با روشهای دیگر به صفحهی خود جذب کند. تنوع زیادی وجود دارد.
از یک منظر – حمله “عمیق” نیست: تمام کاری که یک هکر انجام میدهد رهگیری یک کلیک است. اما از منظری دیگر، اگر هکر بداند که پس از کلیک، کنترل دیگری ظاهر میشود، ممکن است از پیامهای حیلهآمیز برای وادار کردن کاربر به کلیک کردن روی آنها نیز استفاده کند.
حمله بسیار خطرناک است، زیرا زمانی که ما رابط کاربری را مهندسی میکنیم، معمولاً پیشبینی نمیکنیم که یک هکر از طرف بازدیدکننده کلیک کند. بنابراین آسیبپذیریها را میتوان در مکانهای کاملاً غیرمنتظره یافت.
- توصیه میشود از
X-Frame-Options: SAMEORIGIN
در صفحات (یا کل وبسایتها) استفاده کنید که قرار نیست در داخل فریمها مشاهده شوند. - اگر میخواهیم اجازه دهیم صفحاتمان در iframe نشان داده شوند، اما همچنان ایمن بمانیم، از پوشش
<div>
استفاده کنیم.