安全標頭詳解

安全標頭是 HTTP 回應中的一行,它改變訪客瀏覽器可以對你的頁面做的事情:只從指定的來源運行腳本、再也不說明文 HTTP、拒絕被嵌入框架、拒絕猜測內容類型。它們都不需要程式碼、函式庫或重啟——然而大多數網站很少傳送、甚至完全不傳送它們,這就是為什麼大多數網站在掃描器檢查時得 F。本文逐個標頭說明每個到底攔截什麼,解釋 F 的流行,並給出以每小時工作量換取最多保護的修復順序。想看自己網站的判定,先運行安全標頭深度檢查,然後對照你的結果閱讀。

標頭是給瀏覽器的指令,不是伺服器的盔甲

先說標頭不是什麼。安全標頭不修補你的應用,不過濾你的流量,也不認證任何人。它是伺服器附在回應上的一個宣告,而瀏覽器——訪客的瀏覽器,在訪客的機器上——從那一刻起執行它。這聽起來很弱,直到你注意到有多少攻擊是由瀏覽器替攻擊者執行的:運行某人注入留言欄位的腳本,把你的登入頁載入到惡意網站上的一個不可見框架裡,因為一個上傳的文字檔案內容看起來像腳本就把它當作 HTML 處理。標頭是伺服器提前告訴瀏覽器的方式:這些動作在這個網站上從來都不合法。

它們也是縱深防禦,應該這樣理解。這個領域的權威參考是 MDN HTTP 文件,它規定了每個標頭的語法和語義,以及 OWASP Secure Headers Project——OWASP Web 安全測試指南中的測試指引正是指向它推薦的值。這兩個來源都不承諾標頭能讓網站安全。它們承諾的是更窄但真實的事情:設定好標頭後,即使應用漏洞給了攻擊者一個立足點,整類由瀏覽器執行的攻擊也會停止運作。

每個標頭到底攔截什麼

Content-Security-Policy 阻止注入的腳本運行。XSS 及其近親——攜帶腳本的儲存型留言、透過被攻陷的第三方標籤注入的盜刷腳本——都需要瀏覽器執行網站從未打算執行的程式碼。CSP 是一個允許清單:script-src 指明腳本可以來自哪些源,其餘一切,包括內嵌腳本和從攻擊者伺服器載入的任何東西,都會被拒絕。它的弱形式很有名:包含 'unsafe-inline' 的策略讓每個內嵌腳本都能運行,包括被注入的那個,這就是為什麼檢查器把這樣的策略判為弱而不是好。CSP 是部署起來最重的標頭,也是攔截最多的一個。

Strict-Transport-Security 阻止降級。訪客第一次輸入你的網域而不帶通訊協定、或者跟隨一個舊的 http:// 連結時,那個請求以明文穿過網路,在途中可以被剝離、重新導向或讀取——會話 Cookie 也在其中。HSTS 告訴瀏覽器:在接下來的 max-age 秒內,把這個主機的每個 http:// URL 在任何東西離開機器之前改寫為 https://,並拒絕憑證錯誤的點擊穿透。一個誠實的局限:HSTS 只保護那些瀏覽器已經見過一次該標頭的訪客(內建在瀏覽器中的預載清單正是為了彌補這第一次造訪的缺口),而且它只在自己透過 HTTPS 到達時才會被遵從。

框架防護阻止點擊劫持。如果你的頁面可以被嵌入框架,惡意網站可以把你真實的介面鋪在它自己一層透明按鈕之下,收割點擊——受害者看到一個看起來無害的頁面,卻批准了一筆轉帳。CSP 指令 frame-ancestors(以及它的前輩 X-Frame-Options: DENY 或 SAMEORIGIN)指明誰可以把你放進框架;其他所有人得到的只是一個空白框架。X-Content-Type-Options: nosniff 阻止一個更安靜的伎倆:瀏覽器歷史上會二次猜測宣告的 Content-Type,在內容看起來對路時把檔案當作腳本或 HTML 執行,從而把一次無辜的上傳變成儲存型 XSS。nosniff 結束了猜測——宣告的類型就是類型。

Referrer-Policy 阻止 URL 洩漏。當訪客從你的頁面點擊到另一個網站時,瀏覽器歷史上會把你頁面的完整 URL 作為 Referer 傳送出去——包括其中坐著的任何密碼重設權杖、搜尋詞或記錄 ID。strict-origin-when-cross-origin 或更嚴格的策略跨站時只傳送源,並且不向容易降級的 http 目標傳送任何東西。Permissions-Policy 阻止功能濫用:它宣告這個網站到底可以使用哪些強大的瀏覽器能力——攝影機、麥克風、地理位置、付款處理器——這樣被注入的框架或腳本就無法以你的名義悄悄請求它們。而獎勵標頭 Cross-Origin-Opener-Policy 切斷你的頁面與跨源彈出視窗之間的 window.opener 連結,把你的瀏覽上下文與可能探測它的頁面隔離開來。

為什麼這麼多網站得 F

因為沒有任何預設設定會傳送這些標頭。一個原裝的 Web 伺服器、一個原裝的框架鷹架、一個原裝的靜態託管:每個發出的回應最多帶有六個中的一兩個,而最有價值的兩個——CSP 和 Permissions-Policy——需要針對具體網站做決定,因此沒有任何預設設定能安全地附帶它們。然後責任被分成三份:框架團隊以為伺服器會加標頭,伺服器團隊以為框架會加,而前面的 CDN 反正會按自己的意願改寫。標頭就這樣悄無聲息地從縫隙中掉下去,因為沒有它們時表面上一切都不壞。一個得 F 的網站通常看起來和工作起來都和得 A 的一模一樣;差別只在某一天某個攻擊需要瀏覽器配合時顯現。

公佈的數字與這個故事吻合。2026-11-04 的每週工具掃描綜述中報導的一個掃描器的公佈樣本發現:65% 的被掃描網站得 F,51% 完全沒有傳送 Content-Security-Policy。請把這些當作它們本來的樣子——一個掃描器的樣本、由第三方公佈,不是整個網路的測量,也不是我們自己的資料——但方向與每個維運者的經驗一致:標頭是那種缺失反而是統計常態的罕見安全控制。這也是這個統計中藏著的好消息:門檻低得在地板上,而爬上去靠的是設定,不是工程。

修復順序:按每小時的影響

按安全標頭深度檢查所顯明的順序工作。第一,一行搞定的:X-Content-Type-Options: nosniff、框架防護和一個合理的 Referrer-Policy——100 分中的 35 分、幾分鐘的工作量,在普通網站上實際上零破壞風險。第二,HSTS——再加 20 分——但只有在核實每個頁面、子網域和遺留重新導向都真的能透過 HTTPS 工作之後,因為長的 max-age 一旦發出就收不回來。第三,Permissions-Policy:列出你的網站真正使用的功能,關掉其餘的;一行細心的設定換 10 分。

最後,而且慢慢來,CSP。它的 35 分是最大的單塊,也是唯一能把你自己的網站搞垮的修復。先把它以 Content-Security-Policy-Report-Only 加報告端點的形式部署,讀一週的違規報告,改寫它標出的內嵌腳本和事件處理常式——或者按 MDN 指引採用 nonce——並只在報告安靜下來後才切換為強制執行。只要你不依賴跨源彈出視窗流程,就順手加上 Cross-Origin-Opener-Policy: same-origin;它在我們的表裡不計分,但是便宜的隔離。然後重新運行檢查,確認你在源站加的標頭真的穿過了它前面的 CDN 和代理。

標頭做不到什麼

它們修不了漏洞,只能縮小攻擊者透過漏洞能做的事情。CSP 不修復那個沒有跳脫輸入的留言欄位;它讓被注入的負載拒絕運行。HSTS 不修復損壞的憑證鏈——那是 SSL 評分器的領域——沒有任何標頭能替代修補、備份或存取控制。把一個完美的標頭等級理解為一棟樓上鎖得很好的大門,而其他樓層仍然需要各自的檢查,那正是 網站健康檢查的用途。

它們也不能一勞永逸。為去年的網站寫的 CSP 會悄悄地攔住今年的付款元件——或者更糟,在匆忙中被用 * 放寬,從此什麼都不再意味著。像對待其餘設定一樣對待標頭集合:納入版本管理、網站變化時評審、在每次平台遷移後重新檢查。檢查只要十秒鐘;每當你頁面前的技術堆疊發生變化時就運行一次,F 的流行就仍然是別人的統計數字。

來源與延伸閱讀

常見問題

我的網站得了 F。從哪裡開始?

從一行搞定的開始:nosniff、框架防護和 Referrer-Policy 在幾分鐘內贏回 100 分中的 35 分,且不會破壞任何東西。然後在 HTTPS 處處核實後上 HSTS,再上 Permissions-Policy,CSP 最後、走 Report-Only 路線。安全標頭深度檢查會為每個缺失的標頭給出確切的 Nginx 或 Apache 設定行,你可以順著自己的結果頁逐項往下做。

Content-Security-Policy 會把我的網站搞壞嗎?

被強制執行的 CSP 可能會,如果它禁止了你的頁面真正需要的內嵌腳本或第三方源——那是策略在對沒人盤點過的程式碼盡職。安全的路線是先 Report-Only:瀏覽器只報告違規而不攔截任何東西,你修好報告暴露的問題,只有在報告安靜後強制執行才上線。永遠不要靠加上 'unsafe-inline' 或萬用字元來消除破壞;那保住了標頭,卻丟掉了保護。

我應該多久重新檢查一次標頭?

每當你頁面前的東西發生變化時——CDN 設定、新的代理、框架升級、託管遷移——以及作為任何定期評審的一部分。標頭住在會被其他變更悄悄覆蓋的設定裡,而失敗模式是隱形的:網站有沒有它們看起來都一模一樣。在每次部署流水線變更後做一次十秒鐘的檢查,是本頁最便宜的保險。