2022年4月25日

點擊劫持攻擊

「點擊劫持」攻擊允許惡意頁面代表訪客點擊「受害站點」。

許多網站都以此方式被駭客入侵,包括Twitter、Facebook、PayPal和其他網站。當然,它們都已經修復了。

這個想法

這個想法非常簡單。

這是如何在Facebook上進行點擊劫持的

  1. 訪客被引導至邪惡頁面,方式不拘。
  2. 該頁面上有一個看似無害的連結(例如“立即致富”或“點擊這裡,非常有趣”)。
  3. 邪惡頁面在該連結上方定位一個透明的 <iframe>,其 src 屬性來自 facebook.com,使得“讚”按鈕正好位於該連結上方。通常是通過 z-index 實現的。
  4. 訪客試圖點擊連結,實際上卻點擊了按鈕。

演示

以下是邪惡頁面的外觀。為了清楚起見,<iframe> 是半透明的(在真正的邪惡頁面中,它是完全透明的)。

<style>
iframe { /* iframe from the victim site */
  width: 400px;
  height: 100px;
  position: absolute;
  top:0; left:-20px;
  opacity: 0.5; /* in real opacity:0 */
  z-index: 1;
}
</style>

<div>Click to get rich now:</div>

<!-- The url from the victim site -->
<iframe src="/clickjacking/facebook.html"></iframe>

<button>Click here!</button>

<div>...And you're cool (I'm a cool hacker actually)!</div>

攻擊的完整演示

結果
facebook.html
index.html
<!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>

在這裡,我們有一個半透明的 <iframe src="facebook.html">,在示例中,我們可以看到它懸浮在按鈕上方。點擊按鈕實際上是點擊了 iframe,但對用戶不可見,因為 iframe 是透明的。

因此,如果訪客在 Facebook 上有授權(通常會開啟“記住我”功能),則會添加一個“讚”。在 Twitter 上,這將是一個“追蹤”按鈕。

這是相同的示例,但更接近現實,<iframe>opacity:0

結果
facebook.html
index.html
<!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>

我們攻擊所需的一切 - 就是將 <iframe> 在邪惡頁面上定位在按鈕正上方。所以當用戶點擊連結時,他們實際上是點擊了按鈕。通常可以通過 CSS 實現。

點擊劫持僅適用於點擊,而不適用於鍵盤

該攻擊僅影響鼠標操作(或類似的,如在移動設備上的點擊)。

鍵盤輸入要難以重定向得多。從技術上講,如果我們有一個要黑客的文本字段,那麼我們可以定位一個 iframe,使文本字段彼此重疊。因此,當訪客試圖專注於頁面上看到的輸入時,實際上是專注於 iframe 內部的輸入。

但接著就會出現問題。訪客輸入的所有內容都將被隱藏,因為 iframe 是不可見的。

當訪客無法看到自己在屏幕上打印的新字符時,他們通常會停止輸入。

老式防禦(薄弱)

最古老的防禦是一小段 JavaScript,禁止在框架中打開頁面(所謂的“防框架”)。

它看起來像這樣

if (top != window) {
  top.location = window.location;
}

那就是:如果窗口發現自己不在最上層,它會自動讓自己處於最上層。

這並不是一個可靠的防禦,因為有許多方法可以繞過它。讓我們來看看其中幾個。

阻止頂部導航

我們可以通過在 beforeunload 事件處理程序中阻止更改 top.location 引起的轉換來進行阻止。

頂部頁面(包含的頁面,屬於駭客)為其設置了一個阻止處理程序,就像這樣

window.onbeforeunload = function() {
  return false;
};

iframe 嘗試更改 top.location 時,訪問者會收到一條消息,詢問他們是否想要離開。

在大多數情況下,訪問者會給出否定的答案,因為他們不知道 iframe 的存在 - 他們所能看到的只是頂部頁面,沒有理由離開。因此 top.location 不會改變!

實踐中

結果
iframe.html
index.html
<!doctype html>
<html>

<head>
  <meta charset="UTF-8">
</head>

<body>

  <div>Changes top.location to javascript.info</div>

  <script>
    top.location = 'https://javascriptinfo.dev.org.tw';
  </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>

沙盒屬性

sandbox 屬性限制的其中一件事是導航。一個帶有沙盒的 iframe 可能不會更改 top.location

因此,我們可以添加帶有 sandbox="allow-scripts allow-forms" 的 iframe。這將放寬限制,允許腳本和表單。但我們省略了 allow-top-navigation,因此禁止更改 top.location

這是代碼

<iframe sandbox="allow-scripts allow-forms" src="facebook.html"></iframe>

還有其他繞過這種簡單保護的方法。

X-Frame-Options

服務器端標頭 X-Frame-Options 可以允許或禁止在框架內顯示頁面。

它必須正確地作為 HTTP 標頭發送:如果在 HTML <meta> 標籤中找到,瀏覽器將忽略它。因此,<meta http-equiv="X-Frame-Options"...> 不起作用。

該標頭可以有 3 個值

DENY
永遠不要在框架內顯示頁面。
SAMEORIGIN
如果父文檔來自同一來源,則允許在框架內顯示。
ALLOW-FROM domain
如果父文檔來自給定域,則允許在框架內顯示。

例如,Twitter 使用 X-Frame-Options: SAMEORIGIN

這是結果

<iframe src="https://twitter.com"></iframe>

根據您的瀏覽器,上面的 iframe 可能是空的,或者向您發出警告,說瀏覽器不允許以這種方式導航該頁面。

顯示已禁用的功能

X-Frame-Options 標頭有一個副作用。其他站點將無法在框架中顯示我們的頁面,即使他們有充分理由這樣做。

因此,還有其他解決方案... 例如,我們可以使用樣式 height: 100%; width: 100%; 將頁面“覆蓋”在 <div> 上,這樣它將攔截所有點擊。如果 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>
  // there will be an error if top window is from the different origin
  // but that's ok here
  if (top.document.domain == document.domain) {
    protector.remove();
  }
</script>

演示

結果
iframe.html
index.html
<!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>

同站點 Cookie 屬性

samesite Cookie 屬性也可以防止點擊劫持攻擊。

具有此屬性的 Cookie 只有在直接開啟網站時才會被送出,而不是透過框架或其他方式。更多資訊請參閱章節 Cookies, document.cookie

如果網站(如 Facebook)的身份驗證 Cookie 具有 samesite 屬性,如下:

Set-Cookie: authorization=secret; samesite

...那麼當 Facebook 在來自其他網站的 iframe 中開啟時,此類 Cookie 就不會被送出。因此攻擊將失敗。

samesite Cookie 屬性在不使用 Cookie 時不會產生效果。這可能允許其他網站輕鬆地在 iframe 中顯示我們的公開、未驗證的頁面。

然而,這也可能允許點擊劫持攻擊在一些有限的情況下生效。例如,一個匿名的投票網站通過檢查 IP 地址來防止重複投票,但不使用 Cookie 來驗證用戶,因此仍然容易受到點擊劫持攻擊。

摘要

點擊劫持是一種“欺騙”用戶點擊受害網站而不知情的方式。如果存在重要的點擊觸發操作,這是危險的。

黑客可以在訊息中發布對其惡意頁面的連結,或以其他方式誘使訪客訪問其頁面。有很多變化。

從一個角度來看 - 攻擊“不深刻”:黑客只是截取一次點擊。但從另一個角度來看,如果黑客知道點擊後會出現另一個控制,那麼他們可能會使用狡詐的訊息來強迫用戶也點擊它們。

這種攻擊非常危險,因為當我們設計 UI 時,通常不會預料到黑客可能代表訪客進行點擊。因此漏洞可能出現在完全意想不到的地方。

  • 建議在(或整個網站上)不打算在框架內檢視的頁面上使用 X-Frame-Options: SAMEORIGIN
  • 如果我們希望允許我們的頁面在 iframe 中顯示,但仍然保持安全,則使用一個覆蓋的 <div>
教程地圖

評論

在發表評論前,請先閱讀此內容...
  • 如果您有建議要改進 - 請提交 GitHub 問題或拉取請求,而不是發表評論。
  • 如果您在文章中有不明白的地方 - 請加以說明。
  • 要插入幾個單詞的程式碼,請使用<code>標籤;若有幾行程式碼 - 請將它們包裹在<pre>標籤中;若超過 10 行 - 請使用沙盒(plnkrjsbincodepen…)