HTTP 安全回應標頭是網站交付給瀏覽器的防護政策。它們能要求 HTTPS、限制腳本與資源來源、阻止未授權頁面嵌入、關閉 MIME Sniffing、縮減 Referer,以及限制相機、麥克風等瀏覽器功能;但每個標頭保護的邊界不同,也可能因錯誤設定造成網站功能中斷或只留下表面上的安全感。

先講結論:安全標頭是瀏覽器防護政策,不是弱點修復替代品
瀏覽器收到 HTTP Response 後,會依回應標頭決定後續行為。Strict-Transport-Security 要求未來連線使用 HTTPS;Content-Security-Policy 限制可載入或執行的資源;frame-ancestors 與 X-Frame-Options 控制頁面能否被其他網站嵌入;X-Content-Type-Options 阻止瀏覽器把內容猜成另一種 MIME Type;Referrer-Policy 則決定導覽或資源請求可帶出多少來源資訊。
這些控制能降低某些攻擊的可利用性或影響,但不能修正根因。CSP 不能取代情境式輸出編碼與 HTML Sanitization;X-Frame-Options 不能取代高風險操作的再驗證;HSTS 不能修補過期憑證;nosniff 也不能把錯誤的 Content-Type 變正確。應把安全標頭視為 Defense in Depth,而不是掃描報告中只求『標頭存在』的核取方塊。
專業做法不是全站複製同一串 Header,而是先分類 Response:會執行 HTML 與 JavaScript 的頁面、純 JSON API、靜態資源、下載檔案、登入與管理介面,各自需要不同的 Policy。接著將設定集中到應用程式、Reverse Proxy 或 Gateway 的可治理位置,建立自動測試、變更審查與瀏覽器複驗。
- 先確認要保護的瀏覽器行為與威脅,再選擇對應 Header
- Policy 依 Route 與 Response 類型設計,不以首頁結果代表整個網站
- 任何標頭都要同時驗證正確值、實際執行效果與功能相容性
- 平台設定與應用程式設定要有唯一 Owner,避免重複、覆寫或互相矛盾
- 把安全標頭納入 CI、部署後 Smoke Test、監控與例外到期流程
一個 Response 到底在哪裡套用安全標頭?
使用者看到的 Response 可能經過 CDN、WAF、Load Balancer、API Gateway、Reverse Proxy、應用程式與 Framework Middleware。任一層都可能新增、刪除、合併或覆寫 Header;Redirect、錯誤頁、Cache Hit、靜態檔案與 Origin 回應也可能走不同路徑。只檢查應用程式程式碼或首頁,無法證明實際使用者收到的 Policy 一致。
設定層應盡量單一化。全站通用且不依內容變動的 Header 可在邊緣或 Proxy 集中管理;需要每個 Response 產生隨機 Nonce、依頁面資源決定 CSP,或依業務 Route 調整 Cache-Control 的 Header,通常由應用程式產生更合理。無論放在哪一層,都要以外部 Client 實際收到的最終 Response 為準。
稽核時應建立『Route × 狀態 × 交付路徑』矩陣,至少抽查正常 200、Redirect、Authentication Error、Validation Error、404、500、靜態資源、下載與 CDN Cache Hit。若某個 Header 只出現在成功頁,攻擊者可能刻意觸發未受保護的替代回應。
| 位置 | 適合責任 | 常見風險 | 驗證重點 |
|---|---|---|---|
| 應用程式/Middleware | Nonce、Route 專屬 CSP、敏感 Cache Policy | Framework 升級或漏掛 Route | 每個 Handler 與錯誤流程 |
| Reverse Proxy/Gateway | 一致的基礎 Header、Server 資訊收斂 | 重複 Header、覆寫應用值 | Origin 與外部回應差異 |
| CDN/WAF | 邊緣一致性與靜態資源 Policy | Cache 保存舊 Policy、不同 PoP 漂移 | MISS/HIT、不同地區與 Host |
| Web Server | 靜態檔案、下載與預設錯誤頁 | 只保護靜態或只保護動態內容 | MIME、404/500、目錄與別名 |
先用決策矩陣判斷哪些 Header 適用
安全標頭不是數量競賽。對會被瀏覽器解析的 HTML,CSP、頁面嵌入限制、Referrer-Policy 與 Permissions-Policy 都有直接價值;對純 JSON API,CSP 多半沒有實質執行面,但 nosniff、正確 Content-Type、敏感資料快取控制與 Transport Policy 仍重要。下載檔案還需 Content-Disposition 與正確 MIME,避免瀏覽器把使用者提供內容當成可執行頁面。
HSTS 是 Host 層級且由瀏覽器記憶的 HTTPS Policy,不應只針對某個頁面思考。相反地,CSP 會隨每個 Document Response 生效,登入頁、後台與公開頁可能需要不同資源來源。COOP、COEP、CORP 則是跨來源隔離組合,只有需要特定瀏覽器能力或嚴格資源隔離時才應導入,不能為了掃描分數直接全站開啟。
| Response 類型 | 核心 Header | 視需求加入 | 不應直接照抄 |
|---|---|---|---|
| HTML 頁面 | CSP、frame-ancestors、nosniff、Referrer-Policy | Permissions-Policy、COOP/COEP | 與實際資源不相容的嚴格 CSP |
| 純 JSON API | nosniff、正確 Content-Type、Cache-Control | CORP、CORS 精確 Policy | 沒有 Document 執行面的 CSP 堆疊 |
| JS/CSS/圖片 | 正確 Content-Type、nosniff、Cache Policy | CORP、完整性驗證 | 敏感頁面的 no-store 套到所有靜態檔 |
| 下載檔案 | Content-Type、Content-Disposition、nosniff | Cache-Control、CORP | 讓使用者內容 inline 執行 |
| 登入/後台 | 嚴格 CSP、frame-ancestors、Referrer-Policy、no-store | Permissions-Policy、COOP | 第三方 Script 與寬鬆萬用來源 |
HSTS 如何把 HTTPS 從選項變成瀏覽器規則?
HTTP Strict Transport Security(HSTS)透過 Strict-Transport-Security 回應標頭,要求支援的瀏覽器在 max-age 期間把同一 Host 的 HTTP URL 自動升級為 HTTPS,並在 TLS 憑證錯誤時直接失敗,不讓使用者點擊略過。Header 只有透過可信 HTTPS Response 傳送時才會被接受,避免攻擊者在不安全 HTTP 連線中注入或清除 Policy。
HSTS 解決的是瀏覽器已知 Policy 後的降級與錯誤繞過,不自動保護首次連線。若使用者第一次仍透過 HTTP 到站,Redirect 前存在被攔截的窗口;HSTS Preload 可縮小這個窗口,但一旦納入瀏覽器清單,移除需要時間,而且所有宣告包含的子網域都必須長期維持有效 HTTPS。
常見範例是 Strict-Transport-Security: max-age=31536000; includeSubDomains。preload 不是一般安全標頭的必要屬性,而是有額外資格與維運承諾的選擇。正式導入前,先找出所有子網域、舊系統、非 Web Service、憑證自動更新與災難復原路徑,否則 includeSubDomains 可能把尚未支援 HTTPS 的服務鎖住。
| Directive | 作用 | 上線前確認 | 主要風險 |
|---|---|---|---|
| max-age | 瀏覽器記憶 HSTS 的秒數 | HTTPS、憑證更新與回復流程穩定 | 長週期內錯誤設定難以立即撤回 |
| includeSubDomains | 同一註冊網域下子網域一併適用 | 所有現有與未來子網域都支援 HTTPS | 遺留子網域中斷 |
| preload | 透過瀏覽器預載清單保護首次連線 | 符合預載要求並接受長期承諾 | 移除不同步且需要等待瀏覽器更新 |
HSTS 應該怎麼漸進上線?
HSTS 是有狀態且可能造成可用性事故的控制,應採漸進式導入。先確認 HTTP 全面 Redirect 到 HTTPS、TLS 設定與憑證監控成熟,再用短 max-age 觀察。期間故意測試憑證續期、備援切換、子網域與舊 Bookmark;確認穩定後逐步增加到月、半年或一年等組織核准週期。
includeSubDomains 應在子網域盤點與 HTTPS 改造完成後才加入。若目標是 Preload,應另外完成資格檢查與變更核准,不把 preload 當作自動化掃描建議的直接修復。回復計畫也不能只寫 max-age=0;已離線、使用舊 Policy 或尚未收到清除回應的瀏覽器,仍可能保留原設定。
- 建立 DNS 與 Certificate Transparency 子網域清單,找出歷史與第三方委外 Host
- 確認 80 Port 只做 HTTPS Redirect,沒有需維持明文 HTTP 的依賴
- 先以短 max-age 上線並監控 TLS Error、客服回報與各區域可用性
- 逐步延長 max-age,再獨立評估 includeSubDomains 與 preload
- 將憑證到期、CA 失效、時間錯誤與災難復原納入演練
CSP 的核心不是網域清單,而是縮小瀏覽器可執行內容
Content Security Policy(CSP)透過 Content-Security-Policy 回應標頭,限制 Document 可從哪些來源載入 Script、Style、Image、Font、Frame、Connection 等資源,也能限制 Base URL、Form 提交與哪些父頁面可嵌入本站。它最常用於降低 XSS 注入後的執行能力與外送資料路徑,但不會消除應用程式中的注入根因。
CSP 應以最小可執行面為目標。default-src 'none' 能建立拒絕預設,再為 img-src、font-src、connect-src 等必要類型個別放行;object-src 'none'、base-uri 'none' 或 'self'、frame-ancestors 與 form-action 能關閉常被忽略的邊界。實際 Directive 必須依應用依賴設計,不能在不知道資源來源時直接複製。
對 Script,優先採每個 Response 重新產生、不可預測的 Nonce 或建置時 Hash,配合適當的 script-src。Nonce 必須是真正隨機且每次回應不同,不能固定寫在 Template 或由 Client 產生;應用程式也不能把同一 Nonce 加到使用者可控制的任意 Script Element。
| Directive | 控制對象 | 常見安全起點 | 易混淆處 |
|---|---|---|---|
| default-src | 未被其他 Fetch Directive 指定的預設來源 | 'none' 或嚴格來源 | 不是所有 Directive 都會回退到它 |
| script-src | JavaScript 來源與執行條件 | Nonce/Hash、必要 Host | unsafe-inline 會大幅削弱保護 |
| connect-src | fetch、XHR、WebSocket 等連線 | 明確 API 與遙測端點 | 不控制瀏覽器導覽 |
| frame-src | 本站可以載入哪些 Frame | 必要嵌入來源 | 與誰可以嵌入本站不同 |
| frame-ancestors | 哪些父頁面可嵌入本站 | 'none' 或明確來源 | 不回退到 default-src |
| base-uri | HTML base Element 可指向哪裡 | 'none' 或 'self' | 可影響相對 URL 解析 |
| form-action | Form 可提交到哪裡 | 'self' 與必要端點 | 不由 connect-src 控制 |
| object-src | Plugin/Object 資源 | 'none' | 舊式內容通常不再需要 |
CSP Report-Only、Nonce、Hash 與 strict-dynamic 怎麼選?
Content-Security-Policy-Report-Only 會評估違規並產生報告,但不阻擋資源,適合盤點現有依賴與先觀察新 Policy。它不是保護模式;正式 Policy 必須透過 Content-Security-Policy 強制執行。部署期間可以同時保留一份 Enforced Policy 與一份更嚴格的 Report-Only 候選 Policy,逐步收斂。
Hash 適合內容固定、可在建置時知道的 Inline Script;Nonce 適合 Server-Side Rendering 且每次 Response 可注入隨機值的頁面。strict-dynamic 讓受 Nonce/Hash 信任的 Script 可載入其依賴,能降低維護大量 Host Allowlist 的負擔,但需評估瀏覽器支援與 Loader 行為。若只是把 unsafe-inline、unsafe-eval、data: 與大量萬用網域全部放行,CSP 即使存在也未必形成有效邊界。
report-to 是較新的報告機制,report-uri 已被規格淘汰但仍常為相容性並行配置。報告端點必須防止大量事件造成成本或可用性問題,並避免把完整敏感 URL、Token 或使用者資料直接寫入日誌。CSP Report 是線索,不是漏洞證明;Browser Extension、舊 Cache 與第三方資源變動都可能產生雜訊。
| 方式 | 適用場景 | 優點 | 限制與風險 |
|---|---|---|---|
| Host Allowlist | 依賴少數固定外部來源 | 容易理解與起步 | 被信任 Host 上的可控內容仍可能被利用 |
| Nonce | 每次動態產生 HTML | 可精準信任本次 Response 的 Script | 必須高熵、每次不同且安全注入 |
| Hash | 固定 Inline Script | 不需 Server 產生 Nonce | 任何內容變更都需更新 Hash |
| strict-dynamic | 受信任 Loader 動態載入依賴 | 降低 Host 清單維護 | 需理解信任傳遞與相容性 |
| unsafe-inline/unsafe-eval | 僅作受控過渡 | 短期相容舊程式 | 顯著削弱 XSS 防護,不是目標狀態 |
為什麼 CSP 不能取代 XSS 修復?
CSP 是注入後的第二道限制,而不是安全輸出 API。應用程式仍需依 HTML Text、Attribute、URL、JavaScript、CSS 等輸出情境使用正確編碼;允許富文字時使用成熟 Sanitizer;前端避免 innerHTML、document.write 與字串型 Event Handler,改用 textContent、setAttribute 的安全情境或 Framework 預設轉義。
只要 CSP 必須信任某個可被攻擊者控制的 Script Host、允許寬鬆 Scheme,或應用程式把攻擊內容套上合法 Nonce,XSS 仍可執行。即使 Script 被擋,HTML Injection 也可能偽造 Login Form、改寫導覽或透過允許的 Image/Form 外送資料。CSP 的價值是降低攻擊成功率與建立違規可觀察性,不是宣告網站沒有 XSS。
frame-ancestors 與 X-Frame-Options 如何阻擋 Clickjacking?
Clickjacking 會把目標頁面嵌入透明或偽裝的 Frame,誘導已登入使用者點擊原本看不到的操作。CSP frame-ancestors 可指定哪些父層 Origin 能嵌入此頁,是現代且可表達多個來源的主要控制;X-Frame-Options 提供 DENY 或 SAMEORIGIN,適合保留給舊瀏覽器的相容性防護。
對支援 CSP 的瀏覽器,Enforced CSP 中的 frame-ancestors 會優先於 X-Frame-Options。兩者應表達一致意圖,例如 frame-ancestors 'none' 搭配 X-Frame-Options: DENY,或 frame-ancestors 'self' 搭配 SAMEORIGIN。ALLOW-FROM 已過時且支援不一致,不應拿來建立現代 Allowlist。
frame-src 與 frame-ancestors 方向相反:frame-src 控制本站能載入哪些子 Frame,frame-ancestors 控制哪些父頁面能載入本站。即使禁止嵌入,高風險交易仍需 Transaction Summary、重新驗證、Anti-CSRF 與伺服器端授權,因為 Frame Policy 無法防止所有社交工程或已取得頁面控制權的攻擊。
| 需求 | CSP frame-ancestors | X-Frame-Options | 說明 |
|---|---|---|---|
| 完全不允許嵌入 | 'none' | DENY | 適合登入、管理與敏感交易頁 |
| 只允許同 Origin | 'self' | SAMEORIGIN | 注意同 Site 不等於同 Origin |
| 允許指定合作方 | 明確列出 Origin | 無等價現代 Allowlist | 依賴 CSP 並測試合作方流程 |
| 控制本站載入的 Frame | 使用 frame-src | 不適用 | 不要與 frame-ancestors 混淆 |
X-Content-Type-Options: nosniff 為什麼仍然重要?
伺服器應為每個 Response 回傳正確 Content-Type。X-Content-Type-Options: nosniff 會要求瀏覽器尊重宣告的 MIME Type,在特定 Script 與 Style 載入情境阻止把不符合預期的內容猜測成可執行資源,也會啟用部分跨來源讀取阻擋機制。
nosniff 不是 MIME 修復器。若 JavaScript 被錯標為 text/plain,瀏覽器可能拒絕載入而造成網站中斷;若使用者上傳 HTML 卻以 text/html Inline 回傳,內容仍可能成為主動頁面。應同時設定正確 Content-Type、必要時使用 Content-Disposition: attachment、將不受信任檔案放在不同 Origin,並避免可執行副檔名與 Inline Render。
| 內容 | 建議設定 | 避免作法 | 驗證 |
|---|---|---|---|
| JavaScript | 正確 JavaScript MIME + nosniff | text/plain 或 text/html | Browser Network 與 Console |
| CSS | text/css + nosniff | 由動態頁錯誤回傳 HTML | 檢查成功與 404 Response |
| JSON API | application/json + nosniff | 敏感 JSON 以 text/html 回傳 | Content-Type 與跨來源行為 |
| 使用者上傳 | 安全 MIME、attachment、隔離 Origin | 在主站 Origin 直接 Inline | 副檔名、內容與下載行為 |
Referrer-Policy 如何減少網址與來源資訊外洩?
瀏覽器在導覽、載入圖片、Script 或其他資源時,可能透過 Referer Request Header 告訴目的站來源頁面。若 URL Path 或 Query 帶有內部物件 ID、搜尋字詞、邀請碼或其他敏感資訊,過度完整的 Referer 可能流向第三方分析、廣告、CDN 或外部連結。根本控制仍是不要把密碼、Session、Reset Token 或敏感個資放進 URL。
Referrer-Policy: strict-origin-when-cross-origin 是實務上平衡功能與隱私的常見起點:同 Origin 傳完整 URL,跨 Origin 只傳 Origin,從 HTTPS 降級到 HTTP 不傳。更敏感頁面可考慮 no-referrer;只需來源統計可用 strict-origin。Policy 可由 Header 設定,也能在 Element 級別細調,但全站基準最好由 Response Header 明確聲明,不只依賴瀏覽器預設。
| Policy | 同 Origin | HTTPS 跨 Origin | HTTPS 降級 HTTP |
|---|---|---|---|
| no-referrer | 不傳 | 不傳 | 不傳 |
| same-origin | 完整 URL | 不傳 | 不傳 |
| strict-origin | 只傳 Origin | 只傳 Origin | 不傳 |
| strict-origin-when-cross-origin | 完整 URL | 只傳 Origin | 不傳 |
| unsafe-url | 完整 URL | 完整 URL | 完整 URL,不建議 |
Permissions-Policy 是功能委派,不是取得使用者權限
Permissions-Policy 可限制頂層頁面與其嵌入 Frame 是否能使用相機、麥克風、地理位置、全螢幕等瀏覽器功能。語法以 Directive 與 Allowlist 表達,例如 Permissions-Policy: camera=(), microphone=(), geolocation=() 代表關閉這些功能。需要第三方 Frame 使用功能時,還要理解 Header Policy 與 iframe allow Attribute 的交集。
這個 Header 只決定某個 Document 是否有資格呼叫功能,不會取代瀏覽器的使用者 Permission Prompt,也不會讓網站自動取得相機或位置。Directive 與瀏覽器支援持續演進,正式設定前應根據 Browser Support 與產品功能選擇,而不是貼上一長串未知或已更名的功能。
最小權限做法是從實際不用的高風險功能開始關閉,再為確定需要的自家頁面或合作 Frame 建立明確例外。對支付、視訊會議、身分驗證或嵌入式服務,應在真實瀏覽器與各主要平台完成 E2E 測試,因為過度限制常以『功能沒有跳權限視窗』的形式出現。
CORS、CORP、COOP 與 COEP 分別控制什麼?
CORS 決定瀏覽器能否把跨 Origin Response 分享給呼叫端 JavaScript;Cross-Origin-Resource-Policy(CORP)由資源端限制 no-cors 跨來源載入;Cross-Origin-Opener-Policy(COOP)隔離頂層視窗與跨來源 Opener 關係;Cross-Origin-Embedder-Policy(COEP)則要求 Document 嵌入的跨來源資源明確允許。它們處理不同邊界,名稱相近但不能互換。
COOP 與 COEP 組合可建立 Cross-Origin Isolated Context,讓頁面使用某些高階瀏覽器能力並降低跨來源資訊洩漏面;同時也可能讓第三方圖片、字型、分析、登入 Popup、支付或嵌入 Widget 失效。導入前應畫出所有跨來源資源與視窗互動,再確認合作來源能以 CORS 或 CORP 正確授權。
| Header/機制 | 主要問題 | 由誰表達 | 常見副作用 |
|---|---|---|---|
| CORS | 呼叫端 Script 能否讀取跨 Origin Response | 資源伺服器 | 錯誤 Allowlist 洩漏資料 |
| CORP | 資源能否被其他 Site/Origin 以 no-cors 載入 | 資源伺服器 | 第三方頁面無法嵌入資源 |
| COOP | 頂層視窗是否與跨來源 Opener 共用 Context Group | Document 伺服器 | OAuth/支付 Popup 互動改變 |
| COEP | Document 是否只嵌入明確允許的跨來源資源 | Document 伺服器 | 未提供 CORS/CORP 的資源被擋 |
敏感 Response 的 Cache-Control 要怎麼設計?
包含帳務、個資、Token、管理資料或使用者專屬內容的 Response,需明確定義 Browser、Shared Proxy 與 CDN 是否可保存。Cache-Control: no-store 是不儲存 Response 的強指示,適合高度敏感且不應留下副本的內容;private 可禁止 Shared Cache 儲存但允許瀏覽器保存;no-cache 並不是不快取,而是重用前必須重新驗證。
不要把 no-store 無差別套到所有資源。靜態版本化 JS、CSS 與圖片適合長期 Immutable Cache;若全部禁止快取會增加延遲與成本,也可能破壞離線能力。反過來,敏感 Response 若只依 URL 不可猜測或前端不顯示來保護,仍可能被 Browser History、共享裝置、代理或錯誤 CDN 規則保存。
快取測試要包含 Authorization、Cookie、Vary、Cache Key、Authenticated 與 Anonymous Response 混用,以及 Logout 後的 Back/Forward 行為。CDN 是否快取往往由邊緣規則決定,不能只看 Origin 的 Header 就下結論。
Set-Cookie 的 Secure、HttpOnly 與 SameSite 也要一起治理
Cookie Attribute 是透過 Set-Cookie Response Header 交付的瀏覽器安全控制。Secure 限制 Cookie 只經安全連線傳送;HttpOnly 阻止一般 JavaScript 讀取 Cookie,能降低部分 XSS 竊取風險但不阻止 XSS 代替使用者發送請求;SameSite 控制跨 Site Request 是否自動附帶 Cookie,可降低部分 CSRF 風險。
Session Cookie 通常應同時設定 Secure、HttpOnly、合理 SameSite、最小 Domain/Path 與明確生命週期。若跨 Site 登入或嵌入流程必須使用 SameSite=None,瀏覽器同時要求 Secure,應再以 Anti-CSRF Token、Origin 驗證與交易再驗證補足。Cookie Prefix 如 __Host- 能進一步約束 Secure、Path 與 Domain,但需確認部署條件。
哪些舊標頭不應再被當成現代防護?
X-XSS-Protection 是舊瀏覽器的啟發式 XSS Filter,現代瀏覽器多已棄用,而且某些模式曾可能改變頁面內容並引入問題。OWASP 建議不要依賴它,常見安全基準是明確設為 0 或不送出,將防護重點放在安全輸出、Sanitization 與現代 CSP。
Expect-CT 已過時,不應再作為新的安全要求;Public-Key-Pins(HPKP)也因容易造成長期自我鎖定與濫用風險而被淘汰。X-Content-Security-Policy、X-WebKit-CSP 與 X-Frame-Options: ALLOW-FROM 都不是現代 Policy 的替代品。看到掃描器要求舊 Header 時,應先確認規則年代與目標瀏覽器,不為了分數重新啟用過時機制。
Server 與 X-Powered-By 等版本資訊可在不影響維運的情況下降低暴露,但隱藏 Banner 不是修補。攻擊者仍可透過行為與其他特徵推測技術棧;真正重要的是保持元件更新、最小化服務面、限制錯誤細節並有可靠資產清冊。
| Header | 狀態 | 建議 | 現代替代 |
|---|---|---|---|
| X-XSS-Protection | 已棄用/可能有副作用 | 設為 0 或移除依組織基準 | 輸出編碼、Sanitization、CSP |
| Expect-CT | 已過時 | 不要新設 | 瀏覽器 CT 生態與憑證監控 |
| Public-Key-Pins | 已淘汰 | 不要部署 | HSTS、憑證自動化與監控 |
| X-Content-Security-Policy | 舊式前綴 | 不要部署 | Content-Security-Policy |
| X-Frame-Options: ALLOW-FROM | 過時且不一致 | 不要使用 | CSP frame-ancestors |
不要全站共用一份 CSP:依 Route 建立 Policy Profile
同一網域內的公開內容、登入、管理後台、付款、文件預覽與純 API 有不同資源依賴與風險。若為了相容最複雜頁面而把所有第三方來源加入全站 CSP,低風險頁的攻擊面會被不必要放大;若為了追求嚴格分數把全部來源關閉,又會破壞真實功能。
建議建立少量可治理的 Policy Profile,而不是每個 Route 自由拼接。例如 Public Content 允許必要 CDN 與分析;Authenticated App 使用 Nonce 與更嚴格 connect-src;Admin 關閉第三方 Script 與 Frame;API 只保留與 Response 類型相關的 Header;File Download 使用獨立 Origin、attachment 與 nosniff。每個 Profile 都要有 Owner、理由、測試與變更流程。
| Profile | 主要特性 | 允許例外 | 測試重點 |
|---|---|---|---|
| Public Content | CSP、Referrer、嵌入限制 | 核准分析與媒體來源 | SEO、分享、第三方載入 |
| Authenticated App | Nonce CSP、no-store/private、安全 Cookie | 必要 API 與 WebSocket | 登入後流程、Token、快取 |
| Admin | 最少第三方、禁止 Frame、嚴格功能權限 | 明確內部服務 | 高權限操作與錯誤頁 |
| Public API | JSON MIME、nosniff、精確 CORS/Cache | 公開文件化來源 | Preflight、錯誤與 Rate Limit |
| File Download | attachment、正確 MIME、隔離 Origin | 必要 Cache 與 CORP | Active Content 與檔名 |
CDN、Gateway 與 Framework 為什麼常讓 Policy 漂移?
多層平台最常見的問題不是完全沒有 Header,而是值不一致。Gateway 加一份 CSP,應用程式又加一份;Proxy 以 append 產生多個 X-Frame-Options;CDN 還在快取舊 HSTS 或 Referrer-Policy;Framework 只對正常頁套 Middleware,錯誤頁由 Web Server 直接回傳。結果可能比單一缺漏更難判讀。
多份 CSP Enforced Policy 會同時生效,整體效果只會更嚴格,不是後面的值簡單覆蓋前面。對其他 Header,重複值的瀏覽器處理可能不一致。治理上應定義哪一層負責哪一個 Header、禁止未核准的 append、將最終 Response Snapshot 納入測試,並在變更 CDN Rule、Gateway Plugin 或 Framework Middleware 時做回歸。
Domain Alias、www/non-www、IPv6、Regional Endpoint、Blue-Green Environment 與直接 Origin Host 都應納入檢查。安全 Policy 只在主要 Host 正確,其他公開入口仍可能成為無保護的替代路徑。
如何安全盤點現況,而不只看首頁?
先從 Route Inventory、Sitemap、API Spec、CDN Log 與 Browser Network Record 建立樣本,涵蓋不同 Template、身分角色、Response 類型與狀態。對每個樣本保存 Status、Redirect Chain、全部 Response Header、Content-Type、Cache 狀態與實際 Browser Console。不要把 curl -I 單一結果視為完整結論,因為 HEAD 可能走不同 Handler。
自動化掃描能找出 Header 缺漏、弱值、重複或常見不相容,但要判斷 CSP 是否真的能阻擋注入、HSTS 是否會破壞子網域、Referrer 是否含敏感資訊,仍需結合應用情境與瀏覽器測試。測試第三方服務時只對已授權資產操作,不以破壞性 Payload 驗證。
| 維度 | 至少包含 | 要保存的證據 |
|---|---|---|
| 狀態碼 | 200、3xx、401/403、404、500 | 完整 Header 與 Body 類型 |
| 內容類型 | HTML、JSON、JS、CSS、圖片、下載 | Content-Type、nosniff、Disposition |
| 身分 | 匿名、一般使用者、管理者 | Cookie、Cache、Route Policy 差異 |
| 交付路徑 | CDN HIT/MISS、Origin、不同 Host | Via/Cache 狀態與最終 Policy |
| 瀏覽器效果 | 載入、Frame、Referrer、Permission、Console | 截圖、HAR、違規報告 |
安全上線流程:從觀察、修正到強制執行
安全標頭上線的目標是建立可持續的 Policy,而不是一次性改設定。流程先分類所有 Response 與第三方依賴,定義每個 Profile 的目標;對 CSP 使用 Report-Only 蒐集真實流量中的必要資源,清理 Inline Script、unsafe-eval 與未知 Host;對 HSTS 先完成 TLS 與子網域治理,再從短 max-age 漸進延長。
Canary 階段選擇內部使用者、單一 Region 或少量流量強制執行,監控 Resource Block、Login/Payment Failure、Browser Console、CSP Report、TLS Error 與支援工單。例外只能以最小 Directive/Origin 加回,並記錄 Owner、業務理由與到期日,避免為了快速恢復而改成星號或 unsafe-inline。
下圖把 Route 分類、Policy Profile、觀察模式、依賴修正、Canary、正式強制與持續監控串成一條可回復流程。任何被阻擋的必要功能回到依賴修正,不直接繞過整份 Policy。

CI/CD 要測什麼,才能防止下一次部署退化?
CI 可以對代表 Route 做 Contract Test,確認必備 Header 存在、值符合 Profile、沒有過時或重複 Header,CSP 不出現未核准 Host、unsafe-inline 或 unsafe-eval。對 Nonce,還要確認同一頁所有受信任 Script 使用本次 Response 的值、兩次 Response Nonce 不同,且 Header 與 Markup 一致。
End-to-End Test 應在真實瀏覽器驗證頁面可載入、必要 API 與 WebSocket 可連線、未核准 Script/Frame 被擋、第三方登入與付款正常、Referer 符合 Policy、敏感頁 Logout 後不被不當快取。部署後再從外部對 CDN HIT/MISS 與各公開 Host 執行 Smoke Test。
| Gate | 測試內容 | 失敗條件 | 證據 |
|---|---|---|---|
| Static Config | Policy Template 與禁止 Directive | 出現萬用來源、舊 Header 或未核准例外 | Config Diff |
| Response Contract | 代表 Route 的最終 Header | 缺漏、重複、值與 Profile 不符 | Header Snapshot |
| CSP Nonce | Response 間唯一性與 Markup 綁定 | 固定、可預測或不一致 | 兩次 Response 比對 |
| Browser E2E | 功能與阻擋行為 | 必要功能失效或惡意 Fixture 未被擋 | Console、HAR、Screenshot |
| Post-Deploy | 公開 Host、CDN HIT/MISS、錯誤頁 | 任一交付路徑退化 | 外部 Probe 紀錄 |
CSP Report 與安全標頭監控要怎麼看?
監控應回答三個問題:Policy 是否如預期送達、瀏覽器是否阻擋非預期行為、合法功能是否受影響。Header Probe 可定期從不同區域抓取代表 Route;CSP Report/Reporting API 可觀察違規;前端錯誤、轉換率、登入成功率與客服事件則揭露功能影響。只有 CSP Report 數量高低,無法判斷安全或可用性。
CSP Report 需要去重與分類。Browser Extension 注入、第三方資源改版、Ad Blocker、舊頁 Cache 與真正的注入嘗試會混在一起。分析時以 Document URI Template、Directive、Blocked Origin、Release、Browser 與時間窗口聚合,對突然出現的新 Internal Host、data:/blob:、未知 Script Source 或高權限頁違規提高優先級。
報告端點本身需限制 Payload、Rate、保存期限與存取權,避免被用來放大流量或收集敏感 URL。若 URL 可能帶 Token 或個資,先從產品設計移除,再於蒐集管線做最小化與遮罩。
發現設定錯誤時,修復順序怎麼排?
先處理會直接暴露敏感資料或允許高影響攻擊的錯誤,例如管理頁可被任意 Frame 嵌入、敏感 URL 完整外送到第三方、使用者上傳 HTML 在主站 Origin 執行、CSP 對攻擊者可控來源完全放行。接著處理全站缺少基礎 Policy、不同交付層不一致、過時 Header 與缺乏監控。
修復 CSP 時,不要先把所有違規來源加入 Allowlist。先確認該資源是否必要、是否能 Self-Host、能否移除 Inline Script、改用 Nonce/Hash,或把第三方服務隔離到低權限 Origin。修復 HSTS 時先解決 TLS 與子網域,而不是直接增加 max-age。修復 nosniff 問題則先改正 MIME 與錯誤頁,再啟用 Header。
| 優先級 | 典型問題 | 立即控制 | 根本修復 |
|---|---|---|---|
| P0/P1 | 敏感頁可任意嵌入、Active Upload 同 Origin 執行 | 關閉入口、隔離 Origin、臨時 DENY | frame-ancestors、下載架構與輸出控制 |
| P1/P2 | CSP 信任攻擊者可控來源、敏感 Referer 外洩 | 移除來源、縮減 URL 資料 | Nonce/Hash CSP、URL 設計 |
| P2 | Route/CDN Policy 不一致 | 修正邊緣規則與 Purge Cache | 集中 Profile 與部署測試 |
| P3 | 資訊 Banner、過時 Header | 移除不必要資訊 | 基準治理與規則更新 |
怎麼判定安全標頭真的修好了?
『Header 已出現』只是第一層。複驗要證明值與目標 Policy 相符、所有代表 Route 與交付路徑一致、瀏覽器實際執行、合法功能正常,而且寬鬆過渡設定已移除。對 CSP,使用受控 Fixture 驗證未核准 Script/Frame 會被阻擋;對 Referrer,觀察目的站真正收到的 Referer;對 HSTS,確認 HTTPS Response、Redirect、子網域與憑證失敗行為。
證據包應包含變更前後完整 Header、Policy 版本與 Owner、Route/狀態/CDN 矩陣、Browser/Version、Console 或 HAR、CSP Report、功能 E2E 結果及仍保留的例外。若只貼掃描器綠色圖示,後續團隊無法重現,也無法知道是修復、掃描範圍變小或規則被抑制。
| 控制 | 技術驗證 | 功能驗證 | 完成證據 |
|---|---|---|---|
| HSTS | HTTPS 上值正確、週期與子網域符合核准 | 所有 Host 與備援可用 | Header、TLS 與憑證演練 |
| CSP | 未核准 Fixture 被擋、Nonce/Hash 正確 | 主要頁面與第三方流程正常 | Policy、Console、Report、E2E |
| Frame Policy | 外部測試頁無法嵌入、核准方可用 | 合法嵌入與高風險操作正常 | Browser Screenshot 與 Header |
| nosniff/MIME | 錯誤 MIME 被擋且正確 MIME 載入 | JS/CSS/下載正常 | Network、Console、下載行為 |
| Referrer/Permissions | 目的站資訊與功能 Allowlist 符合設計 | 分析、支付、相機等必要流程正常 | Request Capture 與跨瀏覽器 E2E |
給開發、平台與資安團隊的安全標頭檢查表
一套成熟的安全標頭基準,應讓團隊知道每個 Header 為何存在、在哪一層產生、哪些 Route 使用哪個 Profile、如何驗證,以及例外何時到期。掃描工具可以持續發現漂移,但 Policy 設計與相容性判斷仍需要產品、開發、平台與資安共同負責。
- 逐類盤點 HTML、API、靜態資源、下載、登入與管理 Route,不只測首頁
- HSTS 從 TLS、子網域與短 max-age 漸進上線,獨立評估 includeSubDomains 與 preload
- CSP 先 Report-Only 盤點依賴,再以 Nonce/Hash 與細分 Directive 強制執行
- frame-ancestors 與 X-Frame-Options 表達一致意圖,不使用 ALLOW-FROM
- 所有內容回傳正確 Content-Type 並啟用 nosniff;不受信任下載使用隔離 Origin
- 明確設定 Referrer-Policy,並從 URL 根本移除 Token 與敏感資料
- Permissions-Policy 與 COOP/COEP/CORP 只依明確需求導入並做相容性測試
- 移除 X-XSS-Protection 依賴、Expect-CT、HPKP 與舊式 CSP 前綴
- 以最終外部 Response 驗證 CDN、Gateway、Redirect、錯誤頁與 Cache Hit
- 將 Header Contract、Browser E2E、CSP Report、例外到期與外部監測納入持續治理
資料來源
- OWASP HTTP Headers Cheat Sheet↗
- OWASP Content Security Policy Cheat Sheet↗
- OWASP HTTP Strict Transport Security Cheat Sheet↗
- W3C Content Security Policy Level 3↗
- RFC 6797 — HTTP Strict Transport Security↗
- MDN — Referrer-Policy↗
- MDN — X-Content-Type-Options↗
- MDN — Permissions Policy↗
- MDN — Cross-Origin-Embedder-Policy↗
- MDN — Cross-Origin-Resource-Policy↗


