這是我們前端最常見的兩種攻擊手段,也是面試中最常考的前端攻擊。這篇文章我用最精煉、最優雅,也是面試官最喜歡的回答方式來講解下 XSS 和 CSRF。
一、XSS(跨站腳本)
原理
攻擊者把 惡意腳本 注入到受信任頁面並被瀏覽器執行,腳本 利用頁面的信任上下文(Cookies、localStorage、DOM)竊取數據或劫持會話。
常見類型
- 反射型(參數或路徑直接反射到頁面並執行)
- 存儲型(惡意內容存儲在服務器,其他用户訪問時觸發)
- DOM-based(客户端不安全的 DOM 操作導致執行,和服務器無關)
最小復現示例(不安全的後端 + 不安全的前端)
後端(Express — 危險示例)
// server.js(示例,僅演示不安全行為)
const express = require('express');
const app = express();
app.get('/search', (req, res) => {
const q = req.query.q || '';
// 直接把用户輸入拼到 HTML 中 —— 危險!
res.send(`<html><body>搜索: ${q}</body></html>`);
});
app.listen(3000);
訪問 /search?q=<script>alert(1)</script> 會執行腳本(反射型)。
前端 DOM XSS(危險)
<div id="out"></div>
<script>
const q = location.search.split('q=')[1] || '';
document.getElementById('out').innerHTML = q; // 不轉義 —— 危險(DOM XSS)
</script>
實戰防範要點
- **輸出編碼(服務器端)**:所有插入 HTML 的內容做 HTML 轉義(
&<>\"')。 - 前端最小化 innerHTML:儘量用框架綁定(React/Vue 的模板)替代
innerHTML。框架框出來的插值(
{value}/{{ value }})會自動做 HTML 轉義,把<、>、&、"、'等關鍵字符替換成實體(<等),從而把攻擊腳本當文本顯示,而不是執行。 - 富文本白名單清洗:對於必須存儲/渲染的 HTML(富文本),後端用白名單 sanitizer(比如
bleach/html-sanitizer),前端再用 DOMPurify 做一次保護,對標籤屬性等進行清洗。 - Content-Security-Policy(CSP)頭部:禁止內聯腳本、只允許可信源。
- HttpOnly Cookie 頭部:token/cookie 設置 HttpOnly,防止被腳本直接讀取(減輕 XSS 後果)。
示例代碼 — 安全改造
後端(Express + 轉義)
const escapeHtml = s => String(s)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
app.get('/search', (req, res) => {
const q = escapeHtml(req.query.q || '');
res.send(`<html><body>搜索: ${q}</body></html>`);
});
前端(若必須渲染 HTML,用 DOMPurify)
<!-- npm install dompurify -->
<script src="https://unpkg.com/dompurify@2.<!--version-->/dist/purify.min.js"></script>
<div id="content"></div>
<script>
// htmlFromServer 來自後端 API,仍需 sanitize
const htmlFromServer = '<img src=x onerror=alert(1)>';
document.getElementById('content').innerHTML = DOMPurify.sanitize(htmlFromServer);
</script>
設置 CSP(Nginx/Express header 示例)
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; object-src 'none';
二、CSRF(跨站請求偽造)
原理
利用用户已登錄且瀏覽器會自動帶上憑證(cookie)的特性,攻擊者誘導用户發起對受信任站點的請求(如通過自動提交表單或圖片請求),從而在用户 名下執行未授權操作。
最小復現示例(攻擊者頁面)
如果 bank.com/transfer 接受 GET 或 POST 並依賴 cookie 驗證,攻擊頁面可這樣寫:
<!-- auto.html(在攻擊者域名上) -->
<form action="https://bank.com/transfer" method="POST" id="f">
<input name="to" value="attacker" />
<input name="amount" value="1000" />
</form>
<script>document.getElementById('f').submit();</script>
用户在已登錄 bank.com 的情況下訪問攻擊頁面時,瀏覽器會自動帶上 bank.com 的 cookie,導致轉賬。
防護要點
- SameSite Cookie:把 session/cookie 設置
SameSite=Lax或Strict(Lax 對 POST 有保護,適配大多數情形)。 - **CSRF Token(同步/雙提交)**:服務端生成隨機 token,響應給前端;敏感請求必須攜帶並校驗該 token。
該 token 不同於 jwt token ,此處的 csrf-token 只為配合 session+cookie 傳統鑑權策略做安全防護。
- 檢查 Origin/Referer:對跨站請求校驗
Origin或Referer頭(通常對 POST/PUT/DELETE 生效)。 - 避免用 cookie 做對外 API 的認證:採用
Authorization: Bearerheader 的 token 機制(只有 JS 能讀/寫),結合 CORS 限制。 - 敏感操作二次確認:密碼/OTP/二次驗證。
示例代碼(Express + scrf token + csurf)
csurf使用 **雙提交驗證機制(CSRF Token)**:
- 服務端生成一個 CSRF Token,放在 cookie 或 session 中。
- 前端每次發 POST/PUT/DELETE 請求要帶上這個 token,常放在請求頭或表單隱藏字段,比如:
X-CSRF-Token: ey2423482374823748234- 服務端校驗 token,是否匹配、是否未過期、是否合法。
後端(Express)
// server.js
const express = require('express');
const cookieParser = require('cookie-parser');
const csurf = require('csurf');
const app = express();
app.use(cookieParser());
app.use(express.json());
app.use(csurf({ cookie: { httpOnly: true, sameSite: 'lax' } }));
app.get('/csrf-token', (req, res) => {
// 返回 token 給 SPA 前端(用於後續請求 header)
res.json({ csrfToken: req.csrfToken() });
});
app.post('/transfer', (req, res) => {
// csurf 中間件會自動校驗請求中的 token(_csrf 字段或 X-CSRF-Token header)
// 執行轉賬邏輯...
res.json({ ok: true });
});
app.listen(3000);
前端 SPA(獲取 token 並在請求頭中發送)
// 初始化時獲取 token
async function init() {
const r = await fetch('/csrf-token', { credentials: 'include' });
const { csrfToken } = await r.json();
window.__CSRF_TOKEN = csrfToken;
}
// 發送受保護請求
async function transfer() {
await fetch('/transfer', {
method: 'POST',
credentials: 'include', // 仍然帶 cookie
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': window.__CSRF_TOKEN
},
body: JSON.stringify({ to: 'bob', amount: 100 })
});
}
只用 SameSite(簡潔替代,適用多數場景),在服務端設置 cookie:
Set-Cookie: session=...; HttpOnly; Secure; SameSite=Lax; Path=/;
這就能阻止絕大多數通過第三方頁面觸發的 POST/跨站敏感操作。
三、XSS 與 CSRF 的關鍵總結
概念:
- XSS:攻擊者注入腳本並可讀取頁面內容(更強),根源是輸出/DOM 不安全。
- CSRF:攻擊者偽造用户請求,無法直接讀取響應,根源是瀏覽器自動帶憑證。
防護:
- 後端統一使用 HTML escape 庫;富文本走白名單 sanitizer。
- 全站 Cookie:
HttpOnly; Secure; SameSite=Lax。 - 對需要的頁面開啓 CSP(report-only 先觀測,再 enforce)。
- SPA:首次獲取 CSRF token 並在後續請求中以 header 發送;服務端檢查
Origin/Referer。 - CI/代碼審查禁止隨意使用
innerHTML/eval/dangerouslySetInnerHTML。 - 對關鍵操作實施二次驗證(密碼/OTP)。