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>
Set-Cookie
Strict模式,就代表說「這個 cookie 只允許 same site 使用,不被其他 cross site request 加上去」,瀏覽器會驗證不在同一個 site 發出的 request,全部不帶上這個 cookie。
Lax模式放寬限制,<a>, <link rel="prerender">, <form method="GET">
,會帶上 cookie,但POST/PUT/DELETE/OPTION不會帶上cookie。
防護策略
- POST API, 並驗證Content-Type
- 服務器驗證request Access-Control-Allow-Origin,擋掉非白名單domain的API請求
- 加上圖形驗證碼、簡訊驗證碼
- CSRF token,服務器端生成csrf_token,client提交form時帶上csrf_token
- Cookie / header設置相同的資訊(攻擊者的沒辦法讀寫目標網站的 cookie)
- 瀏覽器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