Cross-Site Scripting(XSS)不是單純讓頁面多出一段 HTML,而是應用程式讓不受信任資料跨越內容與程式碼的邊界,使瀏覽器在網站來源的安全脈絡中執行攻擊者控制的內容。有效防護必須知道資料最後進入 HTML、Attribute、URL、JavaScript、CSS 或 DOM 的哪一種情境,再選擇正確的輸出編碼、安全 API 或 HTML Sanitization。

先講結論:讓資料只被顯示,不被瀏覽器當成程式執行
CWE-79 將 XSS 描述為:不受信任資料進入 Web 應用程式,應用程式在產生頁面時沒有防止其中的內容被瀏覽器執行,受害者開啟頁面後,內容便在該網站的來源脈絡中運作。根因不是某一個特殊字元,而是資料被放進可以改變文件結構或執行程式碼的位置。
安全設計要同時回答兩個問題。第一,產品是否只需要顯示純文字?如果是,應使用框架預設自動跳脫、情境式輸出編碼與 textContent 等安全輸出方式。第二,產品是否真的需要讓使用者提交富文字 HTML?如果需要,則應使用成熟且持續更新的 HTML Sanitizer,限制允許的標籤、屬性與 URL Scheme。
OWASP 強調沒有單一技術能處理所有 XSS 情境。最佳基線是框架安全預設、靠近輸出位置的情境式編碼、安全 DOM Sink 與必要時的 HTML Sanitization;CSP、Trusted Types、Cookie 屬性與 WAF 則放在縱深防禦層。
- 先辨識資料的來源、生命週期與最後輸出情境
- 純文字使用情境式輸出編碼與安全 DOM API
- 需要富文字時使用成熟 HTML Sanitizer,不自行以正規表示式清理 HTML
- 禁止外部資料進入 Script、事件處理器、Tag/Attribute 名稱等危險情境
- 用 CSP、Trusted Types、HttpOnly 與監控限制失守後的影響
- 修復後以原始條件、同根因範圍與正常輸入共同複驗
為什麼 XSS 特別危險?程式碼會在受信任網站的 Origin 中執行
瀏覽器以 Origin 區分網站安全邊界。當注入內容在受害網站頁面中執行,它通常能以該頁面既有權限讀取 DOM、觀察畫面內容、呼叫同源 API、操作表單、修改介面或代表目前使用者執行網站允許的動作。這也是為什麼 XSS 不是單純的畫面破版。
HttpOnly Cookie 會阻止 JavaScript 直接讀取該 Cookie,但不會阻止惡意程式利用瀏覽器自動攜帶 Session 呼叫同源功能,也不會保護頁面中的 CSRF Token、Local Storage、非 HttpOnly Cookie 或敏感 DOM 內容。影響要依受害者角色、可見資料、可執行功能與安全控制實際評估。
管理後台、客服介面、內容審核、工單系統與多人協作產品通常風險較高,因為低權限使用者提交的內容可能由高權限人員開啟。只在輸入者自己的瀏覽器出現的問題,則要進一步確認是否能影響他人;不能把所有在 Console 中貼入程式碼的 Self-XSS 都當成產品弱點。
- 讀取頁面上目前使用者可見的個資、交易、Token 或商業資料
- 代表使用者呼叫同源 API、修改設定或提交操作
- 竄改介面、插入假表單或引導使用者完成錯誤動作
- 影響檢視已儲存內容的其他使用者、客服或管理者
- 把既有帳號權限與應用程式功能轉化為更大的業務影響
Stored、Reflected、DOM-based 有什麼差別?
三類 XSS 的核心風險相同,但資料路徑不同。Reflected XSS 通常把請求中的輸入直接帶回同一次 HTTP 回應;Stored XSS 先把輸入存入資料庫、CMS、日誌或其他儲存,再由另一個頁面或使用者讀取;DOM-based XSS 則主要發生在瀏覽器端,前端程式把 URL、Storage、postMessage 等來源送進會解析 HTML 或執行程式碼的 Sink。
分類不是互斥的漏洞嚴重度。Stored XSS 常能在受害者未點擊特製連結的情況下觸發,且可能影響高權限檢視者;Reflected XSS 仍可能透過可信網站連結、轉址或社交工程造成影響;DOM-based XSS 可能完全不把有害值送到伺服器,因此傳統只看 HTTP 回應的掃描器容易漏掉。
實務報告應同時記錄『資料是否儲存』與『執行發生在伺服器產生的 HTML,還是瀏覽器端 DOM 資料流』,不要只用一個標籤取代完整路徑。
| 類型 | 資料路徑 | 常見觸發 | 檢測重點 |
|---|---|---|---|
| Reflected XSS | Request 輸入進入同一次 Server Response | 受害者開啟帶有特定輸入的連結或提交請求 | 比對請求與回應,確認輸出情境及實際瀏覽器解析 |
| Stored XSS | 輸入先儲存,再由其他頁面、角色或時間讀取 | 使用者開啟留言、個人資料、工單、報表或後台內容 | 追蹤寫入與所有讀取點,使用不同角色驗證 |
| DOM-based XSS | 瀏覽器端 Source 經 JavaScript 流向危險 Sink | 頁面載入、Hash/URL 變更、postMessage 或前端互動 | 分析 Client-side Data Flow、DOM Sink 與實際瀏覽器行為 |
| Blind XSS | 儲存輸入在不可見的內部介面或第三方系統觸發 | 客服、稽核、日誌、監控或管理人員檢視資料 | 需在授權測試環境建立安全回呼與跨角色觀測 |
| Self-XSS | 使用者自行在 Console 或欄位執行內容 | 通常需要受害者主動貼上程式碼或繞過警告 | 確認是否存在可影響他人的產品資料流,避免誤報 |
XSS 從哪裡進來?不要只測搜尋框與留言欄
不受信任資料可能來自 URL Query、Path、Fragment、表單、JSON、Cookie、Referer、檔案名稱、Webhook、第三方 API、CMS、資料庫紀錄或即時訊息。資料即使已存進內部系統,也不會因此變成可信;當它在另一個輸出點進入不同情境,仍需要相對應的防護。
DOM-based XSS 特別需要盤點 Source 與 Sink。常見 Source 包括 location、document.referrer、Storage、postMessage 與 API 回應;危險 Sink 則包括 innerHTML、outerHTML、insertAdjacentHTML、document.write、eval 類介面、字串形式的計時器與動態 Script URL。真正要查的是資料是否能從 Source 到達 Sink,以及沿途是否有正確、不可繞過的轉換。
一個值可能在同一產品中被輸出多次:先出現在 HTML 文字,之後進入 Attribute,再被 JavaScript 讀取並寫回 DOM。第一次做過 HTML Encoding,不代表後續 JavaScript 或 URL 情境仍然安全;每一次跨入新解析器都要重新檢查。
| 資料來源 | 可能輸出位置 | 容易忽略的路徑 |
|---|---|---|
| URL 與導覽狀態 | 搜尋結果、錯誤頁、Breadcrumb、前端 Router | Fragment 不會送到伺服器,但可能被前端寫入 DOM |
| 使用者內容 | 留言、名稱、簡介、工單、富文字、檔名 | 後台、通知信、匯出報表與審核預覽使用不同模板 |
| HTTP 與瀏覽器狀態 | Cookie、Referer、User-Agent、Local/Session Storage | 分析、客服或偵錯頁會顯示原本不在 UI 的值 |
| 跨視窗訊息 | postMessage、Iframe、Web Worker | 未驗證 Origin,或把 message.data 直接送入 HTML Sink |
| 第三方與內部資料 | API、Webhook、CMS、資料庫、日誌 | 來源被稱為內部,但內容最初仍可能由外部控制 |
| 前端元件狀態 | Template、Tooltip、Modal、Markdown、圖表標籤 | 安全字串在外掛或元件內被重新解碼、拼接或解析 |
怎麼安全檢測 XSS?先以非執行標記定位資料流
XSS 測試必須建立在合法授權、明確資產範圍與停止條件上。第一階段不需要執行程式碼,可先為每個輸入使用唯一且不具執行能力的標記,追蹤它是否出現在回應、DOM、Attribute、Script、URL、CSS、管理後台或其他角色的頁面,並記錄瀏覽器最後如何解析。
只有在隔離或核准環境、且需要證明實際可執行性時,才使用無害的本地效果作為最小證據,例如改變專用測試元素或設定測試旗標;不要擷取 Cookie、讀取真實資料、對外送出內容、呼叫敏感 API 或影響其他使用者。Stored/Blind XSS 測試要使用專用帳號、資料與回呼端點,並在完成後清除測試內容。
對 DOM-based XSS,單看伺服器回應找不到完整證據。需要在真實瀏覽器中觀察 Source 到 Sink 的資料流、DOM 變化與 JavaScript 執行點,並結合 SAST、IAST、Browser Instrumentation 或程式碼審查。測試時一次只改一個變因,避免把框架錯誤、外掛行為或瀏覽器擴充功能誤判為網站弱點。
一筆可信的 XSS 發現需要哪些證據?
報告不能只附一張彈出視窗截圖。至少要記錄資產、路徑、HTTP 方法、輸入欄位、角色、租戶、環境、瀏覽器與版本,並說明資料從哪個 Source 經過哪些轉換,最後進入哪個輸出情境或 DOM Sink。對 Stored XSS,還要分開記錄寫入者與觸發者。
證據應保存正常基線、非執行標記的原始輸入、Server Response、最終 DOM、相關 Network/Console/CSP Report,以及最小無害驗證結果。若使用瀏覽器開發工具取得證據,要註明觀察的是原始 Response 還是 JavaScript 執行後的 DOM,兩者可能完全不同。
影響評估要以受害者權限與可達功能為基礎。可以證實頁面執行非預期內容,不代表已證實所有帳號都可被接管;反過來,即使 HttpOnly 阻止讀取 Cookie,惡意內容仍可能代表使用者操作同源功能。報告應分開列出已證實行為、合理可達影響與未驗證假設。
| 證據 | 應記錄內容 | 保護與判讀要求 |
|---|---|---|
| 資產與角色 | Host、Path、方法、參數、角色、租戶、環境、版本 | Stored XSS 分開記錄寫入與檢視角色 |
| 資料流 | Source、Server/Client 轉換、Template/Component、最終 Sink | 區分原始 Response 與執行後 DOM |
| 輸出情境 | HTML Text、Attribute、URL、JavaScript、CSS 或 DOM API | 說明目前防護為何不符合該情境 |
| 最小驗證 | 不具外洩或破壞能力的可重複瀏覽器效果 | 不讀取真實資料、不呼叫敏感 API、不影響第三人 |
| 影響與控制 | 受害者權限、可見資料、同源功能、CSP、Cookie 與 Sanitizer | 區分根因防護與縱深控制 |
| 複驗資料 | 修復版本、部署範圍、原始條件、同根因位置與正常回歸 | 提供安全重測條件,不公開可濫用細節 |
核心修復一:依輸出情境使用正確的 Encoding
Output Encoding 的目的,是把不受信任資料轉成瀏覽器只會顯示、而不會解讀為文件結構或程式碼的形式。關鍵是 Encoding 必須依最後輸出情境選擇;HTML Text、HTML Attribute、URL、JavaScript 與 CSS 由不同解析器處理,錯用情境可能留下弱點或造成雙重編碼。
最好的起點是使用現代框架的預設模板輸出,讓框架以文字方式處理變數,並避免繞過機制。若需要自行輸出,則使用成熟且能明確指定 Context 的 Encoder,並盡量靠近真正 Render 的位置執行,而不是在 Request 入口統一把所有輸入先做一次 HTML Encoding。
有些位置本質上過於危險,例如未受控的 Script 內容、事件處理器、Tag 名稱、Attribute 名稱、HTML Comment、CSS Selector 或可執行 URL。這些地方不是換一種 Encoding 就能普遍安全,設計上應禁止外部資料進入。
| 輸出情境 | 主要防護 | 設計注意事項 |
|---|---|---|
| HTML Text | 框架自動跳脫或 HTML Entity Encoding | 純文字優先使用 textContent,不以 innerHTML 顯示 |
| 安全 HTML Attribute 值 | Attribute Encoding 並以引號包住完整值 | Attribute 名稱固定,禁止 onEvent 等可執行屬性 |
| URL 參數 | 先對參數做 URL Encoding,再依外層 Attribute 編碼 | 驗證 Scheme,只允許產品所需的安全協定 |
| JavaScript 資料值 | 優先移出 Inline Script;必要時限定在引號資料值並做 JS Encoding | 不可把外部資料放進程式結構、事件處理器或 eval 類介面 |
| CSS Property 值 | 限制合法值並使用 CSS Encoding | 禁止外部資料控制 Selector、Property 名稱或可載入資源的危險值 |
| 富文字 HTML | 成熟 HTML Sanitizer 與嚴格允許清單 | Sanitize 後不可再拼接、解碼或交給會改寫 HTML 的不可信元件 |
安全資料流:輸入經情境式編碼後,只能進入內容區
下圖將核心原則視覺化:不受信任輸入先經過對應輸出情境的轉換,成為只供顯示的文字資料,再放進瀏覽器的內容區;執行區保持封閉,輸入無法改變頁面程式結構。這個控制應位於最接近輸出的 Template、Component 或 DOM 更新點。
相反地,若資料在入口已做 HTML Encoding,後來卻被解碼、拼進 JavaScript,或交給 innerHTML 重新解析,原本的保護就可能失效。資料跨越每一個 Parser 與信任邊界時,都要重新確認目前 Context 與安全 Sink。

核心修復二:需要富文字時使用 HTML Sanitization
文章編輯器、留言格式、商品說明或 Markdown 預覽有時需要保留連結、段落、清單與部分樣式。若將所有 HTML 全部 Encoding,功能就會失去格式;這時應使用經廣泛檢驗的 HTML Sanitizer,移除或改寫可執行標籤、危險屬性、URL Scheme 與瀏覽器解析邊界。
不要用正規表示式、字串取代或自製黑名單解析 HTML。瀏覽器容錯解析、SVG/MathML、編碼、Namespace、DOM Clobbering 與 Mutation XSS 會讓簡單規則難以完整處理。OWASP 建議使用成熟 Sanitizer,例如 DOMPurify,並持續更新,因為瀏覽器行為與繞過方式會改變。
Sanitize 後的內容也不能被再次破壞。若後續程式拼接未清理片段、重新解碼、改寫 Attribute,或第三方元件以不同方式解析 Sanitized HTML,原本的保護可能失效。系統應記錄 Sanitizer 版本與設定,建立固定惡意樣本回歸測試,並在更新瀏覽器或編輯器後重新驗證。
- 依產品需求建立最小 Allowlist,只保留必要標籤、屬性與安全 URL Scheme
- Sanitization 在可信邊界集中執行,前後端責任與版本清楚
- Sanitize 後不再與未清理字串拼接,也不交給會重新解碼的元件
- 定期更新 Sanitizer 與瀏覽器相容設定,追蹤安全公告
- 對既有儲存內容安排重新清理或 Render-time Sanitization 策略
DOM-based XSS:優先改用安全 Sink,不要只在 Source 過濾
DOM-based XSS 的根因通常是前端把可控字串送進會解析 HTML 或執行程式的 API。較可靠的修復是改變 Sink:顯示純文字使用 textContent;建立節點使用 createElement 與 appendChild;設定固定且無執行能力的 Attribute 時使用 setAttribute;處理 URL 時使用 URL API 並驗證 Scheme。
innerHTML、outerHTML、insertAdjacentHTML 與 document.write 會解析 HTML;eval、Function Constructor 與字串形式的計時器會把資料當程式;Script、Iframe 與部分 URL 屬性也可能形成執行路徑。若功能不需要解析,就不應使用這些介面。
不能只在 location.hash 或 message.data 進入時做一次通用過濾,因為同一資料可能流向多種 Sink。應用 Source-to-Sink 分析找出所有資料流,在 Sink 前使用符合 Context 的控制;接收 postMessage 時還要驗證 Origin、資料結構與允許動作。
| 需求 | 優先使用 | 避免或嚴格限制 |
|---|---|---|
| 顯示純文字 | textContent | innerHTML、document.write |
| 建立 DOM 結構 | createElement、appendChild、固定 Component Template | 以字串組合完整 HTML |
| 設定一般屬性 | 固定 Attribute 名稱的 setAttribute 或型別化 Property | 事件屬性、任意 Attribute 名稱 |
| 建立 URL | URL/URLSearchParams、Scheme Allowlist | javascript:、未驗證 data:、字串拼接 Script URL |
| 解析富文字 | 受控 Sanitizer 產生的 Trusted HTML | 把原始使用者 HTML 直接送入解析 Sink |
| 執行程式邏輯 | 函式與結構化資料 | eval、Function、字串形式 setTimeout/setInterval |
React、Vue、Angular 等框架為什麼仍會出現 XSS?
現代框架通常會對模板中的文字變數自動跳脫,降低最常見的 Server/Client Rendering XSS。但框架不會自動保護所有情境:開發者繞過輸出保護、注入 Raw HTML、信任未驗證 URL、直接操作 DOM、使用不安全第三方元件或建立 Template Injection,都可能重新打開執行路徑。
React 的 dangerouslySetInnerHTML、Vue 的 v-html、Angular 的 bypassSecurityTrust 系列、Lit 的 unsafeHTML,以及任何直接操作 innerHTML 的封裝,都應視為高風險 Escape Hatch。使用前要證明資料來源、Sanitizer、允許清單與輸出位置安全,並限制成少數可審查的共用元件。
Server-side Rendering、Hydration 與前端狀態序列化也要納入。把未信任資料直接嵌入 Inline Script、JSON 若以錯誤 Content-Type 回傳、或把伺服器傳來的字串再次解析成 HTML,都可能讓框架預設保護失效。
- 建立 Raw HTML/Trusted HTML 的集中元件與審查規則,不允許任意散落
- 禁止將使用者輸入直接放入 Inline Script、Event Handler 或動態 Template
- 對 href、src、style 與第三方元件另外執行 URL/CSS/元件安全檢查
- 用 SAST/Lint 尋找危險 API 與框架 Escape Hatch,並建立 Code Owner
- 更新框架、Sanitizer、Markdown Renderer 與富文字編輯器的安全版本
CSP 與 Trusted Types:把瀏覽器變成第二道閘門
Content Security Policy 可以限制哪些 Script 能執行、資源可從哪裡載入,並透過 Report-Only 或 Reporting 收集違規訊號。較強的策略通常以每次回應的 Nonce 或可信 Script Hash 為核心,搭配 strict-dynamic、object-src 'none' 與 base-uri 'none';單純列出大量 Host,或保留 unsafe-inline,常會削弱防護。
CSP 是縱深防禦,不是 XSS 根因修復。錯誤設定、允許來源上的可控 Script、JSONP、第三方供應鏈與既有 Gadget 都可能形成繞過;部分 DOM 操作也可能不需要載入新的外部 Script。應先修正 Encoding、Sanitization 與 Sink,再逐步導入 CSP。
Trusted Types 可在支援的瀏覽器中要求 innerHTML、document.write、Script URL 等 Injection Sink 不接受普通字串,只能接受經核准 Policy 產生的 TrustedHTML/TrustedScriptURL 等型別。它能把散落的 DOM 寫入集中到可審查 Policy,但 Policy 若直接放行輸入仍不安全;導入時應先盤點違規、以 Report-Only 找出舊程式,再讓 Policy 委派給可靠 Sanitizer。
| 控制 | 主要作用 | 不能取代什麼 |
|---|---|---|
| 情境式 Output Encoding | 讓純文字在指定 Context 中只被顯示 | 不能安全保留任意富文字,也不能跨 Context 重用 |
| HTML Sanitization | 保留允許的富文字並移除可執行結構 | 不能保護後續再次拼接、解碼或其他非 HTML Context |
| 安全 DOM Sink | 避免把字串當 HTML 或程式解析 | 不能修正其他仍使用危險 Sink 的路徑 |
| CSP | 限制 Script 執行與資源來源,提供違規報告 | 不能取代 Encoding、Sanitization 與安全 Sink |
| Trusted Types | 在支援瀏覽器限制高風險 DOM Sink 的字串輸入 | 不能保證自訂 Policy 本身正確,也不是跨瀏覽器唯一防線 |
| HttpOnly/SameSite Cookie | 限制部分 Token 存取或跨站請求情境 | 不能阻止頁面內程式執行與同源操作 |
| WAF | 偵測或暫時阻擋部分已知 Payload | 不能可靠防止 DOM XSS、編碼繞過與應用程式根因 |
為什麼全域 Filter、WAF 與封鎖關鍵字不是完整修復?
全域 Request Filter 不知道資料最後會進入 HTML、Attribute、URL、JavaScript 還是 CSS,因此無法選擇正確 Encoding。若先做 HTML Encoding,之後資料進入 JavaScript Context,防護可能不適用;如果開發端又在輸出時編碼,還可能造成 O'Hara、Unicode 或富文字內容被雙重轉換。
以黑名單封鎖 Script、事件名稱、引號或特定字串,無法涵蓋瀏覽器容錯解析、不同編碼、Namespace、URL Scheme、DOM API 與未來語法;同時容易擋住合法內容。WAF 更看不到只在瀏覽器內由 location.hash、Storage 或 postMessage 形成的 DOM XSS。
這些控制可用於緊急降低暴露、發現攻擊跡象或阻擋已知模式,但工單仍應保留永久修復:找到實際輸出 Context、改用安全 Sink、加入正確 Encoder 或 Sanitizer,並在修復後依原始資料流複驗。
如何把 DAST、SAST、IAST 與人工測試組合起來?
DAST 能從執行中的網站探索輸入、反射與瀏覽器行為,適合找公開端點與實際 Render 問題;若支援真實瀏覽器與 JavaScript Instrumentation,對 DOM-based XSS 的覆蓋會更好。但登入、角色、富文字工作流、後台觸發與 Blind XSS 仍取決於 Crawl、測試資料與跨角色流程。
SAST 可搜尋框架 Escape Hatch、危險 DOM Sink 與 Source-to-Sink Data Flow,較容易找尚未由測試流量觸發的程式路徑;IAST 在測試執行期間觀察資料流與 Sink,能降低定位成本,但仍受功能測試覆蓋限制。人工測試則適合驗證複雜 Context、Parser 差異、Stored/Blind 流程與實際影響。
最完整的做法是在開發階段以安全元件、Lint、SAST 與 Code Review 防止新問題,在測試環境用 IAST/DAST 與跨角色測試驗證,正式環境則用 CSP Report、前端 Error、WAF 與異常行為監控補充可見度。任何單一工具的『未發現』都不能證明不存在 XSS。
修復後如何複驗?不只確認彈出效果消失
XSS 複驗要以原始 Source、角色、輸出位置、瀏覽器與觸發流程重測,確認原始輸入在 Server Response 與最終 DOM 中都只被當成資料。若修復只是 WAF 阻擋、CSP 阻止 Script 執行或頁面不再顯示錯誤,底層不安全 Sink 可能仍然存在。
接著檢查同根因範圍:相同欄位是否也出現在搜尋、列表、詳細頁、後台、通知、預覽、匯出與行動版?共用 Markdown Renderer、Tooltip、Modal、表格或 Raw HTML 元件是否仍會解析?DOM Source 是否能從其他 URL、Storage 或 postMessage 路徑到達同一 Sink?
最後要做正常回歸。合法的特殊字元、多語系、連結、Markdown 與允許的富文字應繼續正確顯示;Sanitizer 不應破壞必要格式;CSP/Trusted Types 不應造成大量 Runtime Error。安全修復必須同時證明不可執行與功能仍可用。
| 複驗面向 | 通過條件 | 不充分的結案證據 |
|---|---|---|
| 原始資料流 | 相同 Source 與觸發流程只產生不可執行內容 | 彈出視窗不再出現 |
| 輸出情境 | 每個 Render 點使用正確 Context Encoding 或安全 Sanitizer | 入口增加通用字元過濾 |
| DOM Sink | 危險 Sink 已移除、封裝或只接受可信型別 | CSP 目前阻止了執行 |
| Stored/跨角色 | 所有儲存內容讀取點、角色與後台頁均重測 | 只測寫入者自己的前台頁 |
| 同根因範圍 | 共用元件、API、行動版、預覽、通知與匯出已納入 | 只修報告中的單一 URL |
| 縱深控制 | Strict CSP/Trusted Types/Cookie/監控按設計運作 | 以 WAF 規則取代程式修復 |
| 正常回歸 | 合法特殊字元、多語系與允許富文字正確顯示 | 只用攻擊型輸入測試 |
如果 XSS 可能已被利用,應轉入事件應變
若 CSP Report、前端 Telemetry、WAF、CDN 或應用程式日誌顯示可疑 Script、異常外連、未知 DOM 修改、敏感 API 操作或高權限使用者開啟可疑內容,處理範圍已超過一般弱點修復。團隊應保存頁面版本、儲存內容、HTTP/瀏覽器/CSP/身分與 API 日誌,建立時間線並限制持續觸發。
Stored XSS 要找出所有儲存位置、受影響 Render 點與可能開啟內容的使用者;必要時下架內容、停用功能、撤銷 Session、輪替 Token/API Key,並檢查帳號設定、交易、權限與資料是否被非預期操作。若 Script 可能接觸敏感資訊,還要依資料分類、契約與法規評估通知義務。
清除單一惡意內容不代表事件結束。還要修正共用根因、搜尋其他同類內容、確認第三方 Script 與依賴沒有被竄改、檢查持久化變更,並以新的安全測試與監控規則驗證攻擊路徑已關閉。
開發與維運團隊的 XSS 防護清單
將 XSS 防護變成設計與交付標準,比在上線後追逐 Payload 更有效。以下清單可加入 Architecture Review、Pull Request、Definition of Done、發布與修復複驗。
- 所有外部與儲存資料都標記信任來源,盤點每一個輸出 Context 與 DOM Sink
- 純文字使用框架預設跳脫、Context Encoder 與 textContent 等安全 API
- 需要富文字時使用集中、最小 Allowlist、持續更新的 HTML Sanitizer
- 禁止 Raw HTML、innerHTML、document.write、eval 類 API 與 Framework Escape Hatch 任意散落
- URL 驗證 Scheme;postMessage 驗證 Origin 與資料結構;避免不受控 Script/Event Context
- 導入 Strict CSP Report-Only、CSP Reporting 與支援環境的 Trusted Types,再逐步強制
- HttpOnly、Secure、SameSite、最小 Token 暴露與前端敏感資料最小化同步落實
- 結合 SAST、DAST、IAST、人工跨角色測試與瀏覽器監控,不以單一工具代替完整覆蓋
- 修復後重測原始資料流、所有同根因輸出點、正常多語系與富文字功能
真正的 XSS 防護,是管理每一次資料進入瀏覽器解析器的邊界
Stored、Reflected 與 DOM-based XSS 看似走不同路徑,最終都回到同一件事:瀏覽器把原本應是資料的內容當成可執行結構。修復者必須知道資料最後進入哪一個 Parser 與 Sink,為純文字使用正確 Context Encoding,為必要富文字使用可靠 Sanitization,並避免危險輸出位置。
當框架安全預設、安全 DOM API、CSP、Trusted Types、Cookie 屬性、監控與修復複驗形成閉環,團隊才能同時降低弱點發生率、限制失守後影響,並用可驗證證據證明問題真正關閉。
資料來源
- OWASP Cross Site Scripting Prevention Cheat Sheet↗
- OWASP DOM based XSS Prevention Cheat Sheet↗
- OWASP Content Security Policy Cheat Sheet↗
- OWASP WSTG — Testing for Reflected Cross Site Scripting↗
- OWASP WSTG — Testing for Stored Cross Site Scripting↗
- OWASP WSTG — Testing for DOM-Based Cross Site Scripting↗
- MITRE CWE-79 — Improper Neutralization of Input During Web Page Generation↗
- MDN — Content Security Policy↗
- MDN — Trusted Types API↗


