Cross-Site Scripting Attacks#
概述#
當 web server 安全無虞時,駭客的下一個目標就是瀏覽器。瀏覽器會忠實地執行網頁中出現的任何 JavaScript 程式碼,因此如果攻擊者能將惡意 JavaScript 注入使用者的瀏覽器,就能造成嚴重危害。這種攻擊稱為 Cross-Site Scripting (XSS)。
透過 XSS,攻擊者可以:
- 讀取或修改網頁的任何部分
- 竊取登入憑證或信用卡號等敏感資訊
- 讀取 HTTP session 資訊,劫持使用者的 session(session hijacking)
- 將使用者重導到惡意網站
本章介紹三種最常見的 XSS 攻擊類型及其防禦方式。
Stored Cross-Site Scripting Attacks#
Stored XSS(儲存型 XSS)是指攻擊者將惡意 JavaScript 寫入資料庫,當其他使用者瀏覽包含該內容的頁面時,惡意程式碼就會在受害者的瀏覽器中執行。
攻擊方式#
- 網站通常從資料庫取出內容來產生 HTML(例如產品資訊、使用者對話)
- 任何來自資料庫的頁面內容都是潛在的攻擊向量
- 攻擊者嘗試將 JavaScript 程式碼注入資料庫,使 web server 在渲染 HTML 時將其輸出
典型範例: 在一個論壇網站中,攻擊者在留言中寫入 <script>alert('Your croissants are limp and sad.')</script>。如果網站在建構 HTML 時未適當 escape,這段 <script> 標籤就會被寫入其他使用者的瀏覽器並執行。
不只是留言區域有風險。任何使用者可控制的內容都是潛在的攻擊途徑,包括使用者名稱、個人檔案頁面、線上評論等。

Figure 7-1: 攻擊者透過留言注入 JavaScript
緩解措施 1:Escape HTML 字元#
防止 stored XSS 的關鍵是 escape(跳脫)所有來自資料庫的動態內容,使瀏覽器將其視為 HTML 的文字內容而非原始 HTML 標籤。
Entity encoding 對照表:
| 字元 | Entity Encoding |
|---|---|
" | " |
& | & |
' | ' |
< | < |
> | > |
經過 escape 後,瀏覽器會在建構 DOM 之後才將 escaped 字元轉換回原始字元,因此不會執行 <script> 標籤。

Figure 7-2: 攻擊者的 script 標籤被寫入受害者的頁面
現代 web framework 的 template engine 預設會自動 escape 動態內容。例如 Embedded Ruby (ERB) template 中,
<%= comment %>語法會自動 escape。要輸出原始 HTML 則需要明確呼叫raw函式(<%= raw comment %>),這樣做會有 XSS 風險。
在 code review 時要特別注意:
- 確認動態內容在 template 中被安全 escape
- 檢查是否有 helper function 或方法會建構原始 HTML 並注入 template
緩解措施 2:實作 Content Security Policy#
Content Security Policy (CSP) 讓你可以控制網站上 JavaScript 的執行方式。XSS 攻擊通常依賴在 <html> 標籤中注入 <script> 標籤(即 inline JavaScript)。
透過設定 CSP,你可以告訴瀏覽器永遠不要執行 inline JavaScript,只執行透過 src 屬性匯入的外部腳本。
透過 HTTP response header 設定:
Content-Security-Policy: script-src 'self' https://apis.google.com透過 HTML <meta> 標籤設定:
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self' https://apis.google.com"
/>這個政策表示瀏覽器只會從同一網域('self')和 apis.google.com 載入 JavaScript,inline JavaScript 不會被執行。若要允許 inline JavaScript,需加入 unsafe-inline 關鍵字。
禁止 inline JavaScript 會迫使開發團隊將所有 JavaScript 移到外部檔案,這其實是現代 web 開發的最佳實踐。如果要逐步遷移,可以先使用 violation report 模式:
Content-Security-Policy-Report-Only: script-src 'self'; report-uri https://example.com/csr-reports這樣瀏覽器會回報違規情況而非阻擋執行,方便團隊找出需要修改的頁面。
應同時使用 escape HTML 和 CSP 作為多層防禦(defense in depth),因為攻擊者很難同時找到未 escape 的內容並且將惡意腳本放到白名單網域上。
Reflected Cross-Site Scripting Attacks#
Reflected XSS(反射型 XSS)與 stored XSS 不同,惡意 JavaScript 不是存在資料庫中,而是透過 HTTP request 注入。當網站將 HTTP request 的一部分顯示在渲染的頁面上時,就可能受到此類攻擊。
攻擊方式#
幾乎所有網站都會將 HTTP request 的部分內容顯示在渲染的 HTML 中。例如搜尋頁面會將搜尋詞顯示在搜尋框上方。
攻擊流程:
- 攻擊者將 URL 中的搜尋參數替換為惡意 JavaScript
- 將這個 URL 透過 email 或留言發送給受害者
- 受害者點擊連結後,瀏覽器開啟該 URL
- 伺服器將惡意程式碼「反射」回頁面中執行
即使是大型公司也可能出現 reflected XSS 漏洞。駭客曾在 Google Apps 管理介面(
admin.google.com)發現此類漏洞。
常見目標#
- 搜尋頁面和錯誤頁面,因為它們通常會將 query string 的部分內容顯示給使用者
Stored vs. Reflected XSS 比較#
- Stored XSS 通常危害更大:一段注入資料庫的惡意 JavaScript 可以反覆攻擊所有瀏覽該頁面的使用者
- Reflected XSS 更常見,因為實作門檻較低
緩解措施:Escape HTTP Request 中的動態內容#
防禦方式與 stored XSS 相同 — escape 網站插入 HTML 頁面的動態內容中的控制字元。無論動態內容來自資料庫還是 HTTP request,都需要以相同方式 escape。
Code review 時經常忽略 reflected XSS 漏洞,因為開發者通常只關注 stored XSS。確保團隊了解這個風險並在審查時注意。
DOM-Based Cross-Site Scripting Attacks#
DOM-based XSS 是相對較新的攻擊形式,隨著豐富的 client-side JavaScript framework 興起而出現。攻擊者透過 URI fragment 將惡意 JavaScript 注入使用者的網頁。
URI Fragment 的運作方式#
URL 的結構為:Protocol://Domain/Path?QueryString#URIFragment
- URI fragment 是 URL 中
#號後面的部分 - 瀏覽器使用它進行頁面內導航(跳到指定
id的 HTML 元素) - Single-page apps(如 Angular、Vue.js、React)使用 URI fragment 記錄和恢復狀態(例如 infinite scrolling 的捲動位置)
關鍵特性#
瀏覽器設計上不會將 URI fragment 發送給伺服器。這意味著:
- URI fragment 不會出現在任何 server-side 程式碼中
- 保護 server-side 程式碼無法防禦 DOM-based XSS
- 攻擊完全發生在 client side,無法透過檢查 web server 日誌來偵測
如果 client-side JavaScript 讀取 URI fragment 的內容並將其未經 escape 地直接寫入 DOM,攻擊者可以:
- 建構一個 URI fragment 中含有惡意 JavaScript 的 URL
- 誘騙使用者訪問該 URL
- 惡意程式碼在使用者的瀏覽器中執行
緩解措施:Escape URI Fragment 中的動態內容#
- 任何從 URI fragment 取得資料並建構 HTML 的 JavaScript 程式碼都容易受到 DOM-based XSS 攻擊
- 必須在將 URI fragment 的值插入 HTML 之前進行 escape,如同處理 server-side 程式碼一樣
- 現代 JavaScript template framework 已意識到此風險,不鼓勵在程式碼中建構原始 HTML
例如在 React 中,要寫入未 escape 的 HTML 需要明確使用名為 dangerouslySetInnerHTML 的函式:
function writeSomeHTML() {
return { __html: "First · Second" };
}
function MyComponent() {
return <div dangerouslySetInnerHTML={writeSomeHTML()} />;
}如果你的 client-side JavaScript 程式碼很複雜,考慮遷移到現代 JavaScript framework(如 React),它們讓程式碼更易管理,安全考量也更明確。同時務必設定適當的 content security policy。
總結#
- XSS 攻擊是指攻擊者將 JavaScript 注入網站頁面,在使用者瀏覽時執行
- 攻擊者通常透過三種途徑注入惡意 JavaScript:資料庫(stored)、HTTP request(reflected)、URI fragment(DOM-based)
- 防禦的核心是 escape 動態內容中的 HTML 控制字元
- 設定 Content Security Policy 防止 inline JavaScript 執行,作為額外的防禦層