Skip to main content

CSRF(跨站點請求偽造)

https://www.cloudflare.com/zh-tw/learning/security/threats/cross-site-request-forgery/

前言

CSRF 全稱是 Cross Site Request Forgery,跨站請求偽造。用戶在駭客網頁,點擊連結,按鈕,表單,執行 http 請求,使用用戶的瀏覽器登入資訊,進行偽造請求。

GET類型 csrf

舉個例子,現在服務器有個刪除 account API,驗證session id成功就能成功執行刪除操作,現在hacker的網頁有個連結長這樣子,用戶點擊後發送delete account API的GET請求,服務器接受請求驗證session id正確成功執行刪除帳號。瀏覽器的機制,你只要發送 request 給服務器,就會把相關的 cookie 資訊一起發送,這 request 看起來就像是用戶本人發出的。

<a href='https://correct-site.com/delete?id=3'>開始測驗</a>

或是

<div>
<img src='https://correct-site.com/delete?id=3' width='0' height='0' />
<a href='https://hacker-site.com/tarot-test'>開始測驗</a>
</div>

POST類型 csrf

假若服務器刪除 account API做成 POST,這樣可以擋住 <a> 或是 <img> 的攻擊了,但若是 <form> HTML的表單提交,網頁代碼如下,hacker成功刪除account。或是看不見的 iframe,讓 form submit 之後的結果出現在 iframe 裡面,而且這個 form 還可以自動 submit,完全不需要經過用戶的任何操作。

<form>能夠帶的 Content-Type 只有三種:application/x-www-form-urlencoded, multipart/form-data, text/plain,服務器驗證Content-Type可以擋住這類型攻擊。

參考:https://stackoverflow.com/questions/17940811/example-of-silently-submitting-a-post-form-csrf

<form action="https://correct-site.com/delete" method="POST">
<input type="hidden" name="id" value="3"/>
<input type="submit" value="開始測驗"/>
</form>

或是

<iframe style="display:none" name="csrf-frame"></iframe>
<form method='POST' action='https://small-min.blog.com/delete' target="csrf-frame" id="csrf-form">
<input type='hidden' name='id' value='3'>
<input type='submit' value='submit'>
</form>
<script>document.getElementById("csrf-form").submit()</script>

Strict模式,就代表說「這個 cookie 只允許 same site 使用,不被其他 cross site request 加上去」,瀏覽器會驗證不在同一個 site 發出的 request,全部不帶上這個 cookie。

Lax模式放寬限制,<a>, <link rel="prerender">, <form method="GET">,會帶上 cookie,但POST/PUT/DELETE/OPTION不會帶上cookie。

防護策略

  1. POST API, 並驗證Content-Type
  2. 服務器驗證request Access-Control-Allow-Origin,擋掉非白名單domain的API請求
  3. 加上圖形驗證碼、簡訊驗證碼
  4. CSRF token,服務器端生成csrf_token,client提交form時帶上csrf_token
  5. Cookie / header設置相同的資訊(攻擊者的沒辦法讀寫目標網站的 cookie)
  6. 瀏覽器cookie防禦,啟用SameSite cookie,預設是Strict模式

偽造請求的代碼例子:

<form action="https://small-min.blog.com/delete" method="POST">
<input type="hidden" name="id" value="3"/>
<input type="hidden" name="csrftoken" value="fj1iro2jro12ijoi1"/>
<input type="submit" value="刪除文章"/>
</form>
xsrfCookieName: 'XSRF-TOKEN', // default

xsrfHeaderName: 'X-XSRF-TOKEN', // default
Set-Cookie: session_id=ewfewjf23o1; SameSite=Strict
Set-Cookie: foo=bar; SameSite=Lax