CSRF for Web Developer

寫給網頁開發者的 CSRF 理解與防範

什麼是 CSRF

CSRF(Cross-Site Request Forgery)跨站請求偽造,意味著攻擊者透過用戶身份發送惡意請求。 具體來說該攻擊利用瀏覽器請求預設會帶上 Cookie 的特性,透過各種手段讓瀏覽器發送用戶不知情的請求來達成目的

舉例來說用戶登入過 A 網站並在瀏覽器 Cookie 留下了敏感資訊,並且又瀏覽了某惡意網站,其中包含了惡意請求代碼,當觸發時就會讓用戶在設計好的惡意頁面 B 對 A 頁面發送惡意捏造的請求。

  • 可以是點擊連結
<a href="http://bank.com/transfer.do?acct=MARIA&amount=100000">View my Pictures!</a>
  • 可以是加載隱藏的圖片
<img src="http://bank.com/transfer.do?acct=MARIA&amount=100000" width="0" height="0" border="0" />
  • 可以是加載隱藏的自動提交表單
<body onload="document.forms[0].submit()">
<form action="http://bank.com/transfer.do" method="POST">
<input type="hidden" name="acct" value="MARIA" />
<input type="hidden" name="amount" value="100000" />
<input type="submit" value="View my pictures" />
</form>
</body>

大概念即是透過不同方式想辦法偽造用戶在其他網站的請求,關鍵字:「跨站」、「請求」、「偽造」。

防範 CSRF

要防範 CSRF 在使用者的角度其實是比較難做到的,多數的防範措施都需要在伺服器端進行,在客戶端最多只能如下防範:

  1. 不讓任何敏感資訊儲存於瀏覽器,每次使用完網站就登出。
  2. 避免進入來路不明的可疑網站。

防範 CSRF 最根本的方式就是「不要相信請求來源自本人」,勢必要添加一些需要用戶互動的驗證機制或是只有用戶本人才能知道的資訊來確認請求的合法性。

CSRF Token

伺服器可以針對不同用戶生成隨機的 CSRF Token 並且在接收請求時比對驗收,因為 CSRF Token 並不存儲於 Cookie,跨站的網頁並沒有辦法獲取因而能夠確認用戶的請求是否為本人。

舉例表單提交前自動夾帶伺服器生成的 CSRF Token,提交時自動送出,只有當 CSRF Token 吻合才是合法的請求:

<form action="http://bank.com/transfer.do" method="POST">
<input type="hidden" name="csrf-token" value="Generate CSRF Token Here..." />
<button type="submit">Submit</button>
</form>

設置 Cookie 的 SameSite 屬性,限制第三方網站對 Cookie 的訪問,這樣一來就無法透過第三方網站發送惡意偽造的請求。不過基於這種作法依賴瀏覽器支援,舉體可以參考 samesite - caniuse🔗

SameSite Cookie 屬性有三種模式,可以根據方便性與安全性進行取捨:

  • Strict :只有當瀏覽者是從同一個網站直接互動(例如點擊連結、輸入網址) 時,才會送出 Cookie。
  • Lax(大多瀏覽器預設值):允許「安全的跨站請求」(像是 GET 連結、表單 submit),但不允許非安全請求(如 POST、PUT、DELETE)攜帶 Cookie。
  • None: 允許任何跨站請求攜帶 Cookie。
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; SameSite=Lax

總結

無論如何都不輕易相信用戶請求即能最大程度防範 CSRF。隨著使用者逐漸使用重視 Cookie 存取安全性的瀏覽器,可以期望 CSRF 可以被更好更簡單的防範。

延伸閱讀