一、原理:
利用用戶登錄態偽造操作
CSRF(Cross-Site Request Forgery,跨站請求偽造)是攻擊者“借刀殺人”,借用用戶瀏覽器中已有的登錄狀態,誘導用戶完成攻擊者指定的操作。
1.?基本機制分解
1)瀏覽器的默認行為:自動帶上 Cookie(包括 sessionid)
當你訪問一個網站并登錄后,例如:
POST /login
Host: bank.com
Cookie: sessionid=abc123
-
網站會通過 Cookie(如
sessionid
)記錄你的登錄狀態。 -
登錄后,每次你再訪問這個網站,瀏覽器都會自動帶上 Cookie:
GET /user/profile
Cookie: sessionid=abc123
這是 瀏覽器的默認行為,不管你怎么發請求,只要是訪問 bank.com
,就自動帶上它的 Cookie。
2)網站服務器的判斷邏輯
網站一般只根據 Cookie 判斷用戶身份:
if request.cookie["sessionid"] is valid:allow_operation()
所以請求 只要帶了合法的 Cookie,網站就以為是“你自己操作的”。
3)攻擊者不能獲取 Cookie,但能讓瀏覽器代發請求
攻擊者 無法直接讀取你的 Cookie,但卻可以通過誘導你訪問惡意頁面,讓瀏覽器替他發出請求。
瀏覽器會在請求中 自動附帶 Cookie,即使這是攻擊者構造的請求!
攻擊者只需要這樣:
<img src="https://bank.com/transfer?to=attacker&amount=10000">
當你一打開這個網頁:
-
瀏覽器會訪問
bank.com
; -
自動附帶你的 Cookie(如 sessionid);
-
請求落到銀行服務器上;
-
銀行服務器識別 Cookie 合法,就執行了“轉賬”。
結果是:你替攻擊者完成了操作,自己還一無所知。
2.?類比解釋:偽造你發指令
想象這個場景:
-
你在一個公司門禁系統中,登錄了一個可以發指令的管理后臺;
-
攻擊者不能進公司,但可以給你發一個“點擊這個鏈接”的誘導郵件;
-
你一不小心點開,就自動觸發了一個“給他打錢”的命令;
-
門禁系統看到是你發的,指令合法,就執行了。
攻擊者沒權限,但他“騙你”用你的權限幫他做了事。
3.?條件總結(CSRF 成功的關鍵前提)
-
?用戶已登錄目標網站,且登錄狀態仍有效(Cookie 存在);
-
?目標網站通過 Cookie 識別身份(常見);
-
?請求沒有額外校驗(如 CSRF token);
-
?攻擊者誘導用戶訪問了惡意鏈接或頁面;
-
?瀏覽器自動發送 Cookie,服務器誤以為是用戶行為。
4.?攻擊流程圖(邏輯順序)
[用戶登錄 bank.com] → [獲得 sessionid Cookie]↓
[訪問惡意網站] → [構造對 bank.com 的請求]↓
[瀏覽器自動帶上 sessionid]↓
[bank.com 收到請求] → [識別為用戶操作] → 執行了操作
5.?攻擊者和用戶的角色區別
項目 | 用戶 | 攻擊者 |
---|---|---|
擁有 Cookie | ?是 | ?否 |
能發請求 | ?是 | ?能騙用戶發 |
能偽造內容 | ?無法偽造 Cookie,但能偽造請求內容 | |
能操作目標站 | (自己操作) | (借助用戶操作) |
總結
CSRF 利用的不是系統漏洞,而是 Web 的信任機制 + 用戶不知情行為 + 瀏覽器的自動 Cookie 行為。
二、構造:
自動提交表單
攻擊者構造一個偽造的表單,提前填好表單字段,讓用戶在訪問時自動提交,借此觸發目標網站上的敏感操作(如轉賬、修改密碼等)。
1.?構造基本結構
<form action="https://bank.com/transfer" method="POST"><input type="hidden" name="to" value="attacker123"><input type="hidden" name="amount" value="10000">
</form><script>document.forms[0].submit(); // 頁面加載后自動提交表單
</script>
2.?構造流程詳解
1)<form>
標簽構建 POST 請求
<form action="https://bank.com/transfer" method="POST">
-
action
是目標地址; -
method="POST"
表示發起 POST 請求; -
請求將帶上表單中的參數。
2)使用隱藏字段構造參數
<input type="hidden" name="to" value="attacker123">
<input type="hidden" name="amount" value="10000">
-
這些字段用戶看不到;
-
但它們會作為 POST 的參數發送出去;
-
模擬的是用戶提交表單的過程。
3)使用 JavaScript 自動提交表單
document.forms[0].submit();
-
頁面加載時立即提交;
-
用戶根本無感知。
3.?構造后發出的請求(抓包效果)
POST /transfer HTTP/1.1
Host: bank.com
Content-Type: application/x-www-form-urlencoded
Cookie: sessionid=abcdef123456to=attacker123&amount=10000
瀏覽器自動帶上用戶登錄狀態中的 Cookie(如 sessionid
),服務器誤以為是用戶自己發出的請求。
4.?實戰演示用法(真實攻擊邏輯)
假設:
目標網站 bank.com
有如下邏輯:
-
POST /transfer
可以轉賬; -
需要參數
to
和amount
; -
只要 Cookie 中的 session 合法就執行操作;
-
沒有 CSRF Token 或 Referer 校驗。
攻擊頁面代碼:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>你中獎了</title>
</head>
<body><h1>恭喜你中獎了!正在處理領獎信息...</h1><form id="csrfForm" action="http://localhost:8000/transfer" method="POST"><input type="hidden" name="to" value="attacker_account"><input type="hidden" name="amount" value="10000"></form><script>window.onload = function() {document.getElementById('csrfForm').submit();}</script>
</body>
</html>
攻擊流程:
-
用戶在登錄了
bank.com
后,未退出; -
用戶訪問了攻擊者構造的釣魚網頁;
-
瀏覽器在加載頁面時,自動提交表單;
-
bank.com
收到合法 Cookie + 合法參數; -
成功執行“給攻擊者轉賬”操作。
5.?關鍵點
特性 | 描述 |
---|---|
請求類型 | POST |
參數構造方式 | 隱藏字段 <input type="hidden"> |
自動觸發方式 | JS:form.submit() |
瀏覽器行為 | 自動攜帶 Cookie |
用戶感知程度 | 無感知,訪問頁面即中招 |
6.?限制與防御
這種方式雖然常見,但也容易被防御:
防御方式 | 是否能阻止這種攻擊 |
---|---|
CSRF Token 校驗 | ?可以阻止 |
Referer 校驗 | ?可以阻止 |
SameSite Cookie 限制 | ?可以阻止 |
登出機制不當 | ?無法阻止 |
用戶粗心點開鏈接 | ?攻擊成功 |
總結
自動提交表單是構造 CSRF 的最主流手段,它依賴瀏覽器自動攜帶 Cookie + 用戶無感知行為,在攻擊頁面中用隱藏字段 + JS 一鍵觸發操作。
=======================================
img/src 請求
<img>
標簽中的 src
屬性,只要被瀏覽器解析,就會自動發出 GET 請求。
攻擊者可利用這一點,構造一段帶有 img 標簽的 HTML 頁面,誘導用戶訪問,讓用戶在不知情的情況下向目標網站發起請求。
最關鍵的是:瀏覽器在發出這個 GET 請求時,會自動攜帶當前域名下的 Cookie(如 sessionid
、token
等登錄態信息)。
如果目標網站沒有做好 CSRF 防御,攻擊就可能成功。
1.?構造示例
<!-- 攻擊者網頁上的 HTML 代碼 -->
<img src="https://bank.com/transfer?to=attacker&amount=10000">
2.?瀏覽器行為分析
-
用戶訪問攻擊者構造的網頁;
-
<img>
標簽被瀏覽器解析; -
瀏覽器自動向
https://bank.com/transfer?...
發起 GET 請求; -
自動帶上當前用戶的 Cookie(如
sessionid=abcdef123456
); -
如果目標接口沒有驗證 Referer、Token,服務器會誤以為是合法操作;
-
攻擊成功。
發出的請求大致
GET /transfer?to=attacker&amount=10000 HTTP/1.1
Host: bank.com
Cookie: sessionid=abcdef123456
User-Agent: Mozilla/5.0 ...
3.?攻擊條件
要讓攻擊成功,必須滿足以下條件:
條件 | 說明 |
---|---|
用戶已登錄目標網站 | 否則沒有有效 Cookie,無法偽造操作 |
Cookie 中記錄了身份憑證 | 如 sessionid 或 JWT |
目標接口使用 GET 實現敏感操作 | 如轉賬、綁定郵箱、刪除內容等(極其不安全) |
沒有 CSRF Token 或 Referer 檢查 | 否則請求會被攔截 |
4.?其它類似的標簽(也能觸發 GET)
攻擊者可以不止使用 <img>
,還可以使用以下方式:
標簽 | 示例代碼 |
---|---|
<img> | <img src="..."> |
<script> | <script src="..."> |
<iframe> | <iframe src="..."> |
<link> | <link rel="stylesheet" href="..."> |
<object> | <object data="..."> |
5.?img/src 的限制
限制點 | 說明 |
---|---|
只能發 GET 請求 | 無法發 POST 請求 |
無法自定義請求頭 | 不能設置 Referer、User-Agent 等 |
無法處理響應 | JavaScript 無法獲取返回內容 |
一旦目標接口有驗證就失敗 | CSRF Token、SameSite Cookie、Referer 限制 |
6.?使用場景
雖然 POST 請求一般更危險,但一些老舊或不規范的網站,可能把敏感操作寫成 GET 請求,比如:
-
GET
/deleteAccount?id=123
-
GET
/addFriend?user=evil
-
GET
/transfer?to=attacker&amount=5000
這類系統就特別容易被 <img>
標簽攻擊。
7.?攻擊場景舉例
1)刪除某用戶帖子:
<img src="https://forum.com/deletePost?id=999">
用戶點擊釣魚鏈接或訪問攻擊頁面后,該帖子被刪除。
2)給攻擊者點贊/加好友:
<img src="https://social.com/addFriend?uid=attacker">
用戶以為自己在看圖片,其實是在無意間幫攻擊者加了好友。
8. 安全建議(從服務端角度)
防御措施 | 有效性 |
---|---|
禁止用 GET 做敏感操作 | ?非常重要 |
使用 CSRF Token | ?推薦 |
檢查 Referer / Origin | ?可選 |
設置 SameSite Cookie | ?可阻止跨站請求 |
用戶操作需二次確認 | ?提高安全性 |
總結
img/src 是最“隱蔽”的 CSRF 構造方式之一,用戶根本意識不到任何操作,但背后其實可能在給別人轉賬、點贊、刪除內容,屬于“無交互感知攻擊”,而瀏覽器的默認行為(自動發 GET、自動帶 Cookie)正是它得逞的根源。
三、? 防御:
Token 驗證
**CSRF Token(跨站請求偽造令牌)**是一段由服務器生成、隨機、不可預測的字符串,用來綁定用戶請求和用戶身份之間的唯一性,防止惡意網站偽造用戶的請求。
它不保存在 Cookie 中,而是嵌入在 HTML 頁面中,并隨著每個請求一起發送回來。
為什么需要 Token?
因為 Cookie 是自動發送的,攻擊者可以構造偽造請求利用用戶的登錄態。而 Token 是攻擊者無法提前獲取、又必須提交的驗證參數。
所以只要驗證這個 Token,服務器就能判斷請求是不是由真實用戶頁面發起的。
1.?工作原理詳解
1)用戶訪問頁面
-
服務器生成一個隨機 Token:
session['csrf_token'] = 'Xyz123Abc...'
-
把這個 Token 插入到 HTML 頁面里(常見方式是 form 隱藏字段):
<input type="hidden" name="csrf_token" value="Xyz123Abc..." />
2)用戶提交表單時
瀏覽器會將該隱藏字段和其他字段一起發送:
POST /transfer HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Cookie: sessionid=abc123to=tom&amount=1000&csrf_token=Xyz123Abc...
3)后端校驗
后端取出提交的 Token 和保存在會話(session)中的 Token 進行比對:
if request.form['csrf_token'] != session['csrf_token']:return 'CSRF ATTACK DETECTED'
一致:允許操作
不一致/缺失:攔截攻擊
2.?為什么能防御 CSRF?
因為:
-
攻擊者不能獲取用戶的 Token(它是嵌入 HTML 的,不能跨域讀取);
-
攻擊者無法猜出 Token 內容(足夠隨機);
-
即使攻擊者構造了相同參數請求,缺了 Token 也無法通過驗證。
3.?示例(完整 Flask 演示)
from flask import Flask, request, session, render_template_string, redirect
import secretsapp = Flask(__name__)
app.secret_key = 'your-secret-key'@app.route('/')
def form():# 生成隨機 CSRF Tokentoken = secrets.token_hex(16)session['csrf_token'] = tokenreturn render_template_string('''<form action="/submit" method="post"><input type="text" name="to" placeholder="轉賬對象"><input type="text" name="amount" placeholder="金額"><input type="hidden" name="csrf_token" value="{{token}}"><input type="submit" value="提交"></form>''', token=token)@app.route('/submit', methods=['POST'])
def submit():if session.get('csrf_token') != request.form.get('csrf_token'):return "CSRF ATTACK BLOCKED"return f"已向 {request.form['to']} 轉賬 {request.form['amount']} 元"if __name__ == '__main__':app.run(debug=True)
4.?建議
方法 | 描述 |
---|---|
每個表單獨立 Token | 提高安全性(避免 Token 重用) |
Token 過期時間 | 防止長期有效的令牌被利用 |
雙 Token 機制 | Cookie + 表單同時攜帶,提升安全性 |
Ajax 請求 | 放在自定義 Header 中,防止被 HTML form 模擬提交 |
注意:GET 請求無法用 Token 防御!
因為 <img>
、<a>
、<script>
標簽都可以發 GET 請求,但無法攜帶 Token,所以:
-
敏感操作必須使用 POST/PUT/DELETE 請求
-
且要配合 Token 驗證
總結
特性 | 描述 |
---|---|
?核心機制 | 最強大的 CSRF 防御方式 |
?獨立于瀏覽器行為 | 不依賴 Cookie 屬性 |
?不影響用戶體驗 | 用戶無感知 |
?實現略復雜 | 需要頁面與服務端同步 Token |
=======================================
Referer 檢查
Referer(原意是 Referrer,拼寫錯了但沿用了)是 HTTP 請求頭的一部分,它記錄了:
這個請求是從哪個頁面跳轉過來的。
例如,在網頁中點擊了一個鏈接或提交了一個表單,請求頭就會帶上:
Referer: https://www.bank.com/account
這個字段就告訴服務器:用戶當前的請求是從 https://www.bank.com/account
頁面發出來的。
1.?Referer 檢查的原理
核心邏輯:只允許來自本站頁面發起的請求。
如果服務端發現請求的 Referer 不是自己的域名,則判定請求不是合法來源,有可能是 CSRF 攻擊。
2.?Referer 檢查怎么實現?
以 Python Flask 為例:
from flask import request@app.route('/transfer', methods=['POST'])
def transfer():referer = request.headers.get('Referer')if referer is None or not referer.startswith("https://www.bank.com"):return "非法請求,可能是 CSRF 攻擊", 403# 執行轉賬操作return "轉賬成功"
3.?攻擊與防御場景對比
攻擊請求(來自攻擊者網站):
攻擊者構造一個頁面,誘導你點擊按鈕:
<form action="https://www.bank.com/transfer" method="POST"><input type="hidden" name="amount" value="10000"><input type="hidden" name="to" value="attacker">
</form>
<script>document.forms[0].submit()</script>
點擊后瀏覽器發出 POST 請求:
POST /transfer HTTP/1.1
Host: www.bank.com
Referer: https://evil.com/attack.html
Cookie: session=123456
Referer 檢查發現不是本域名,攔截。
正常請求(正常用戶操作):
用戶在 https://www.bank.com/account
頁面點擊“轉賬”,發起請求:
Referer: https://www.bank.com/account
服務器一看,是本站頁面發來的,請求放行。
4.?Referer 檢查的優缺點
優點:
優點 | 描述 |
---|---|
簡單 | 不需要在頁面嵌入 Token,服務器代碼也很簡單 |
快速生效 | 服務端只需增加 Referer 檢查邏輯即可 |
對用戶透明 | 不影響用戶體驗,不需要頁面修改 |
缺點:
缺點 | 描述 |
---|---|
Referer 可能為空 | 某些瀏覽器或隱私插件會去掉 Referer,導致誤判 |
有些合法請求可能是跨域 | 比如 OAuth 登錄回調、第三方支付跳轉,Referer 不是本站但合法 |
攻擊者不能偽造 Referer,但可以構造沒有 Referer 的請求 | 比如通過 <img> 、<iframe> 發起請求,瀏覽器可能不帶 Referer |
不適用于 API 接口 | 移動端請求或者 AJAX 跨域接口可能沒有 Referer,造成誤判或不適用 |
5.?實際使用建議
Referer 檢查作為 輔助防御機制 是很有價值的,但不能作為唯一防線,因為:
-
它容易被繞過(Referer 空值、瀏覽器兼容性等);
-
有時候會攔正常請求;
-
安全級別不如 CSRF Token 或 SameSite Cookie。
6.?最佳實踐
場景 | 建議 |
---|---|
Web 表單提交 | 推薦使用 CSRF Token + Referer 檢查雙重防御 |
AJAX 請求 | 建議加 CSRF Token 頭部或參數驗證 |
開放接口(如 API) | 不建議使用 Referer,推薦使用 OAuth / 簽名機制等 |
總結
Referer 檢查是一種基于請求來源的 CSRF 防御手段,通過驗證請求的 Referer
是否來自本站,攔截非本站發起的請求。雖然簡單有效,但存在兼容性和安全邊界問題,建議作為 CSRF Token 的補充手段。
=======================================
SameSite Cookie
SameSite
是 Cookie 的一個屬性,用來控制瀏覽器在“跨站請求”時是否發送這個 Cookie。
它是由瀏覽器實現的安全策略,不依賴后端邏輯。主要用來防御跨站攻擊(特別是 CSRF)。
跨站請求是什么意思?
舉例說明:
-
你在瀏覽
https://evil.com
,這上面有一個表單偷偷向https://bank.com/transfer
發 POST 請求。 -
瀏覽器默認會攜帶你在
bank.com
登錄時留下的 Cookie(含 session id)! -
bank.com
后臺認為你是登錄用戶,直接處理請求了!這就是 CSRF!
SameSite 屬性有哪幾種值?
SameSite 值 | 描述 | 是否允許跨站請求帶 Cookie |
---|---|---|
Strict | 最嚴格,完全禁止 | ?不允許任何跨站請求帶 Cookie |
Lax | 默認,兼容性好 | ?允許 GET 請求帶 Cookie,禁止 POST |
None | 不限制 | ?始終允許,但必須配合 Secure (即 HTTPS) |
1.?各模式詳細解讀
1)SameSite=Strict
只允許 同站請求(用戶主動點擊本站鏈接、表單提交等),否則瀏覽器不會附帶 Cookie。
可防御所有類型的 CSRF!
缺點:
-
用戶從其他站點點擊鏈接訪問本站后,可能需要重新登錄。
2)SameSite=Lax
略微放寬,允許以下情況攜帶 Cookie:
-
鏈接跳轉(GET)
-
資源加載(img、iframe)
-
表單或腳本的 POST?不攜帶 Cookie(可防御 CSRF)
優點:
-
大多數網站默認使用這個值,兼容性好;
-
可防御大多數 CSRF(POST 被阻止)。
3)SameSite=None
這是唯一允許完全跨站請求帶 Cookie的選項。
但有個前提:必須同時設置 Secure
屬性,且使用 HTTPS,否則瀏覽器會拒絕設置 Cookie!
如果不加 Secure
,Chrome 直接不保存這個 Cookie!
2.?設置示例(Flask)
@app.route("/set_cookie")
def set_cookie():resp = make_response("設置成功")# SameSite 可以設置為 'Strict' / 'Lax' / 'None'resp.set_cookie("session_id", "123456", samesite="Lax", secure=False)return resp
如果設置 SameSite=None,必須加 secure=True
,并啟用 HTTPS。
3. SameSite 的 CSRF 防御原理
瀏覽器發起跨站請求(如:在惡意網站中自動提交表單):
-
如果
SameSite=Strict
或Lax
,瀏覽器不會附帶 session_id -
后端檢測不到登錄態,CSRF 請求失效?
4.?實際建議
場景 | SameSite 推薦值 |
---|---|
普通用戶站點(需防 CSRF) | Lax (推薦) |
管理后臺系統(高安全性) | Strict |
第三方服務需帶 Cookie | None + Secure + HTTPS |
5. 檢查?Cookie 設置
打開瀏覽器控制臺(F12 → Application → Cookies),可以看到:
-
Name
:session_id -
Value
:xxxx -
SameSite
:Strict / Lax / None -
Secure
:是否開啟
總結
SameSite 是瀏覽器級別的防御機制,用來讓“跨站請求時 Cookie 不再自動發送”,從根源上限制 CSRF 攻擊。