2026 年現代 Web 應用中的跨站腳本攻擊(XSS)與 CSP 安全標頭實作
from 'next/server';
im from 'node:cry' 's'`,
});
res;
from 'next/)
d;` }}
/>
</>
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
always 參數非常重要,它確保即使是 4xx 或 5xx 回應也會帶上安全標頭。很多人只在 200 回應上設定,結果錯誤頁面成為防護缺口。
若使用 Cloudflare,可以透過 Transform Rules 動態改寫回應標頭,或是在 Workers 中實作更細緻的 nonce 注入邏輯。對於需要動態 nonce 又不想改動應用程式碼的團隊,用邊緣運算做 HTML 重寫是一個常見折衷方案:Worker 攔截回應,產生 nonce,把標頭中所有合法的 <script> 標籤補上 nonce 屬性,再送出。
3-4 四階段漸進式導入流程
第一階段:盤點。列出所有外部資源網域、所有行內腳本與行內樣式、所有使用 eval 或 new Function 的位置、所有動態產生的 HTML 片段。這份清單決定了你的政策能有多嚴格。
第二階段:Report-Only 觀察。使用 Content-Security-Policy-Report-Only 部署完整政策,只回報不阻擋。連續觀察至少一至兩週,涵蓋各種使用者流程與裝置。把回報中的違規逐一分類為「需要修正的程式碼」與「需要放行的合法來源」。
第三階段:收斂與修正。把行內腳本改為外部檔案或加上 nonce;把 eval 依賴替換掉;移除不必要的第三方來源;為第三方腳本加上 SRI。每修正一項,就縮小一次政策。
第四階段:強制執行與監控。切換為強制模式後,持續監控回報,並把 CSP 違規納入告警系統。建議每次部署新功能時,都先在 Report-Only 環境測過一次。
3-5 常見破版原因與排查清單
font-src 與 img-src 是否包含 CDN 網域,注意 data: 與 blob: 需要明確列出。style-src 未允許行內樣式。若使用 CSS-in-JS 函式庫,請確認它是否支援 nonce 注入。connect-src,WebSocket 需要 wss: 協定前綴。frame-src 與 form-action,並確認 OAuth 流程是否需要開啟彈窗(window.open 不受 CSP 限制,但新視窗中的頁面有自己的政策)。eval 或注入的除錯腳本。請在正式環境設定中明確移除 'unsafe-eval' 並確認建置產物不含相關程式碼。四、縱深防禦:CSP 之外你還需要做的事
CSP 是一道強力的防線,但它不是唯一的一道。真正穩固的防禦需要多層機制互相補位,任何一層被突破時,其他層仍能發揮作用。
4-1 依上下文的輸出編碼(Context-Aware Output Encoding)
這是對抗 XSS 的根本手段。所謂「依上下文」,意思是根據資料最終被放置的位置,採用不同的編碼策略:
&、<、>、"、' 轉為對應的 HTML 實體。href、src,尤其要阻擋 javascript:、data:text/html、vbscript: 等協定。<、>、&、以及 U+2028、U+2029。現代做法是優先使用 JSON.stringify 搭配 < 的轉義。encodeURIComponent,並對整個 URL 做白名單驗證。對於必須接受富文本的場景(例如論壇文章、商品描述),請使用經過社群驗證的消毒函式庫(如 DOMPurify),並採用「允許清單」模式而非「黑名單」模式。
4-2 完整的安全標頭組合
CSP 應與以下標頭一起使用,形成完整的防護網:
max-age=63072000; includeSubDomains; preload。注意 preload 是不可逆的,確認所有子網域都支援 HTTPS 後再啟用。camera=()、microphone=()、geolocation=()、payment=()。SharedArrayBuffer 等高效能 API。HttpOnly(阻擋 JavaScript 讀取)、Secure(只透過 HTTPS 傳輸)、SameSite=Lax 或 Strict(緩解 CSRF)。若需要跨站使用,可以考慮 __Host- 前綴。4-3 第三方腳本治理與子資源完整性
第三方腳本是你最難控制的一環,但可以透過以下手段降低風險:
integrity 與 crossorigin 屬性,確保檔案未被篡改。缺點是第三方更新版本時需要同步更新雜湊值。對於經常變動的腳本,可以改用 CSP 的 hash 機制或內部代理。defer 或 async 載入,或延後到使用者互動後才載入,縮小攻擊視窗。五、2026 年 CSP 導入檢查清單
以下清單可以直接拿來當作團隊的導入驗收標準:
是否已完整盤點所有外部資源網域與行內腳本?
是否已使用 Report-Only 模式觀察至少一至兩週?
是否已為每個回應產生唯一且不可預測的 nonce?
是否已確認含 nonce 的 HTML 不會被 CDN 或 Service Worker 快取?
是否已啟用 strict-dynamic 並移除不再需要的來源白名單?
object-src 'none'、base-uri 'none'、frame-ancestors 'none'?'unsafe-inline' 與 'unsafe-eval'?default 政策進行觀察?是否已設定 Reporting API 並將違規報告接入監控系統?
是否已為所有第三方腳本加上 SRI 或改用內部代理?
是否已在 CI/CD 流程中加入安全標頭的自動化檢測?
是否已建立 CSP 變更的回滾機制與責任人?
六、常見問答
Q1:導入 CSP 會不會嚴重影響網站效能?
不會。CSP 的檢查是瀏覽器端的輕量比對,對效能影響微乎其微。真正可能影響效能的是「改用外部腳本檔案」帶來的額外請求,但這可以透過 HTTP/2 或 HTTP/3 的多工、以及正確的快取策略來抵銷。
Q2:如果我的網站大量依賴行內樣式,該怎麼辦?
優先改用外部樣式表。若無法避免(例如需要動態計算的主題變數),可以為 <style> 標籤加上 nonce。對於框架自動注入的行內樣式,請確認框架是否有支援 nonce 的設定選項,或考慮在部署時抽出樣式。
Q3:CSP 真的能完全防住 XSS 嗎?
不能。CSP 是「緩解」而非「根治」。它能大幅提高攻擊門檻、阻擋大部分自動化掃描工具,但若應用本身存在伺服器端渲染的注入、CSP 政策配置錯誤、或允許了可被濫用的來源,攻擊者仍有機會繞過。因此 CSP 必須與輸出編碼、Trusted Types、Cookie 保護等措施搭配使用。
Q4:Report-Only 模式要觀察多久?
至少涵蓋一個完整的業務週期(例如一週),若能涵蓋月結、促銷活動等流量高峰更好。重點是讓各種使用者流程、裝置與瀏覽器版本都被涵蓋到。
Q5:Trusted Types 在非 Chromium 瀏覽器上怎麼辦?
不支援的瀏覽器會忽略 require-trusted-types-for,網站仍能正常運作,只是少了那一層保護。你仍應在程式碼中使用 policy 建立信任型別,因為這部分在所有瀏覽器上都能正常運作——差別只在於是被強制還是自願遵守。
結語:安全不是一次性任務,而是一條持續收斂的曲線
2026 年的 Web 安全環境比以往任何時候都複雜。前端框架不斷推陳出新,AI 讓程式碼產出的速度遠超過安全審查的速度,第三方腳本構成了難以完全掌握的供應鏈,而攻擊者的工具同樣在進化。在這樣的環境下,期待「寫出絕對不會有 XSS 的程式碼」是不現實的;更務實的目標是:建立多層防禦,確保任何單一環節的失誤都不會直接導致災難性後果。
CSP 正是這個思路的具體實踐。它不要求你一次到位——你可以從 Report-Only 開始,逐步收斂政策;可以先從 object-src 'none' 和 base-uri 'none' 這兩條低風險指令入手;可以在有餘力時再導入 nonce 與 strict-dynamic;最後再挑戰 Trusted Types。每一步都能帶來實質的安全增益,而且每一步都是可逆、可觀察、可回滾的。
最後想提醒的是:安全政策如果沒有人維護,就會慢慢腐化。今天嚴格的政策,可能因為某次緊急上線被加入了 'unsafe-inline',然後就再也沒有人移除。建議把 CSP 違規報告納入日常監控,把安全標頭的檢查納入程式碼審查清單,並在每次架構變更時重新評估政策的適用性。當安全變成開發流程的一部分,而不是事後補救的負擔時,它才真正有可能長期維持下去。
願各位的網站,既跑得快,也守得住。
```