Link Search Menu Expand Document

3. CSRF 跨站請求偽造

3-1 什麽是 CSRF 跨站請求偽造?

請用 admin@example.org 帳號登入,瀏覽任一活動:

image

管理員可以給留言 Highlight 高亮,這樣就會顯示黃色背景。

駭客沒有辦法辦法騙管理員去高亮特定文章呢? 如果還有 XSS 漏洞的話,可以用 XSS 攻擊,留下一段 JavaScript 代碼去觸發那個高亮按鈕,只要管理員逛到那一頁,執行到 JavaScript 的話,也可以辦到這件事情。

不過,上一節我們已經防禦好 XSS,這一節我們用另一條攻擊手法。請先觀察你要高亮那一篇留言:

image

這一篇留言的ID是 522,所以要騙管理員去高亮這篇留言,就是要騙管理員的瀏覽器去打 /events/92/comments/522/highlight 囉,這件事情其實很簡單,駭客留言一下:

<img src="/events/92/comments/522/highlight">


image

image

上一節防止 XSS 時,有用 sanitize 來做白名單的過濾,而 img 標籤是一個白名單放行的標籤,因此這個圖檔瀏覽器會發送 Request 請求到 /events/92/comments/522/highlight 去抓圖。當然,我目前的身份是 hacker@example.org 是沒有權限高亮的,但是已經挖了一個坑準備給管理員跳。

app/controllers/comments_controller.rb 中,我們有用 before_action :require_admin!, :only => [:highlight] 檢查調用 highlight 方法必須有管理員權限。

接著請切換到管理員 admin@example.org 身份,去瀏覽這一頁,然後重新整理頁面一次:

image

這篇文章就被高亮了….. 用管理員的權限去高亮的。瀏覽器真好騙啊。

這種攻擊,就叫做CSRF 跨站請求偽造,駭客挖坑讓有權限的用戶去跳。

3-2 如何防禦?

首先,這個高亮會這麽好觸發的原因,在於我們把它設計成 GET 請求,這樣駭客用 img 標籤太容易就能誘騙瀏覽器了。讓我們把它改成 POST 操作。

  resources :events do
    resources :comments do
      member do
-       get :highlight
+       post :highlight
      end
    end
  end

以及

-  <%= link_to "Highligh", highlight_event_comment_path(@event, comment), :class => "btn btn-default" %>
+  <%= link_to "Highligh", highlight_event_comment_path(@event, comment), :method => :post, :class => "btn btn-default" %>

在 Web API 設計實作課程的 3-2 什麽是 REST API 之中,也有講述到 GET 和 POST 的差異:GET 只能單純讀取資料,不應該修改資料。而 POST 則是執行某個操作,會修改到伺服器的資料。

這樣的話,用 img 標籤就不能攻擊了,駭客必須用到 JavaScript 才能讓管理員的瀏覽器去做 POST 請求。但是要用 JavaScript 的話,就必須存在有上一節的 XSS 漏洞才行了。

3-3 漏網之魚

CSRF 之所以危險的地方,在於攻擊方可以在其他網站挖坑,只要管理員瀏覽到那一頁,一樣可以執行這個攻擊。

所以就算上一節我們已經改成 POST 了,而且這個網站也不存在 XSS 漏洞,駭客依然有辦法去誘使管理員的瀏覽器去 POST 操作。例如駭客知道管理員常去看「百度貼吧」,然後假設「百度貼吧」有個 XSS 漏洞可以貼 JavaScript 代碼,那麽駭客可以貼文:

<iframe style="display:none" name="csrf-frame"></iframe>

<form method='POST' action='http://localhost:3000/events/92/comments/522/highlight' target="csrf-frame" id="csrf-form">
  <input type='submit' value='submit'>
</form>

<script>document.getElementById("csrf-form").submit()</script>

這段 HTML 和 JavaScript 代碼會自動 submit 到隱藏的 iframe 視窗裡面,所以管理員是沒有感覺的。只要管理員瀏覽到這段貼文的網頁,瀏覽器就會自動 POST 出去了…. 達成高亮效果。

3-4 如何防禦 POST 類型的 CSRF 攻擊?

那要如何防禦呢?我們需要針對所有非 GET 的操作,都加上額外的 token 驗證碼參數,檢查用戶瀏覽器當初真的是從網站上的某個表單送出的,而不是不小心觸發的 CSRF 攻擊。

這個防禦功能 Rails 5.2 之後是預設開啟。若是在 API 的場景中不需要這個功能,要特別用 skip_before_action :verify_authenticity_token 來關閉。

若是 Rails 5.2 之前的版本,則請修改 app/controllers/application_controller.rb,打開 protect_from_forgery

  class ApplicationController < ActionController::Base

    # protect_from_forgery with: :exception
+   protect_from_forgery with: :exception

於是在 Rails 產生的表單中,就會帶這個參數 authenticity_token

image

如果是 Ajax 請求,jQuery 則會去抓 meta 中的 csrf-token 參數附加上去:

image

透過這種機制,駭客在其他網站挖的坑因為沒有這個authenticity_token參數,送過來的時候就會被 Rails 阻擋住,會有以下的錯誤畫面:

image

這也是為什麽有時候逛 Rails 製作的網站,表單如果放太久才送出,有時候也會出現這個錯誤畫面,因為這個 authenticity_token 過期失效了。


Copyright © 2010-2022 Wen-Tien Chang All Rights Reserved.