這是我們前端最常見的兩種攻擊手段,也是面試中最常考的前端攻擊。這篇文章我用最精煉、最優雅,也是面試官最喜歡的回答方式來講解下 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>

實戰防範要點

  1. ​**輸出編碼(服務器端)**​:所有插入 HTML 的內容做 HTML 轉義(&<>\"')。
  2. 前端最小化 innerHTML​:儘量用框架綁定(React/Vue 的模板)替代 innerHTML

    框架框出來的插值({value} / {{ value }})會​自動做 HTML 轉義​,把 <>&"' 等關鍵字符替換成實體(&lt; 等),從而把攻擊腳本當文本顯示,而不是執行。

  3. 富文本白名單清洗​:對於必須存儲/渲染的 HTML(富文本),後端用白名單 sanitizer(比如 bleach / html-sanitizer),前端再用 DOMPurify 做一次保護,對標籤屬性等進行清洗。
  4. Content-Security-Policy(CSP)頭部​:禁止內聯腳本、只允許可信源。
  5. HttpOnly Cookie 頭部​:token/cookie 設置 HttpOnly,防止被腳本直接讀取(減輕 XSS 後果)。

示例代碼 — 安全改造

後端(Express + 轉義)

const escapeHtml = s => String(s)
  .replace(/&/g, '&amp;')
  .replace(/</g, '&lt;')
  .replace(/>/g, '&gt;')
  .replace(/"/g, '&quot;')
  .replace(/'/g, '&#39;');

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,導致轉賬。

防護要點

  1. SameSite Cookie​:把 session/cookie 設置 SameSite=LaxStrict(Lax 對 POST 有保護,適配大多數情形)。
  2. ​**CSRF Token(同步/雙提交)**​:服務端生成隨機 token,響應給前端;敏感請求必須攜帶並校驗該 token。

    該 token 不同於 jwt token ,此處的 csrf-token 只為配合 session+cookie 傳統鑑權策略做安全防護。

  3. 檢查 Origin/Referer​:對跨站請求校驗 OriginReferer 頭(通常對 POST/PUT/DELETE 生效)。
  4. 避免用 cookie 做對外 API 的認證​:採用 Authorization: Bearer header 的 token 機制(只有 JS 能讀/寫),結合 CORS 限制。
  5. 敏感操作二次確認​:密碼/OTP/二次驗證。

示例代碼(Express + scrf token + csurf)

csurf 使用 ​**雙提交驗證機制(CSRF Token)**​:

  1. 服務端生成一個 CSRF Token,放在 cookie 或 session 中。
  2. 前端每次發 POST/PUT/DELETE 請求要帶上這個 token,常放在請求頭或表單隱藏字段,比如:X-CSRF-Token: ey2423482374823748234
  3. 服務端校驗 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​:攻擊者偽造用户請求,無法直接讀取響應,根源是瀏覽器自動帶憑證。

防護:

  1. 後端統一使用 HTML escape 庫;富文本走白名單 sanitizer。
  2. 全站 Cookie:HttpOnly; Secure; SameSite=Lax
  3. 對需要的頁面開啓 CSP(report-only 先觀測,再 enforce)。
  4. SPA:首次獲取 CSRF token 並在後續請求中以 header 發送;服務端檢查 Origin/Referer
  5. CI/代碼審查禁止隨意使用 innerHTML/eval/dangerouslySetInnerHTML
  6. 對關鍵操作實施二次驗證(密碼/OTP)。