接上篇文章説道,跨域解決方案中的 CORS 方案,會配置一個
Access-Control-Allow-Origin的配置項,而且我們一般不直接配置為*,這樣做的原因是什麼以及企業中的最佳實踐是怎麼樣的,這篇文章給你答案!
簡單概括
Access-Control-Allow-Origin: *允許任意來源的頁面讀取你的響應內容(對瀏覽器端的 JS 可讀性開放)。- **如果響應包含敏感數據或依賴 cookie/憑證(Authorization / session)**,絕不能 用
*。因為*與Access-Control-Allow-Credentials: true不能同用,瀏覽器也會拒絕這種組合;但即便沒有 credentials,開放讀取會增加數據泄露與濫用風險。 - 對於公共靜態資源(可公開的圖片、腳本、樣式),用
*是可接受且常見的做法;但對API/用户數據/鑑權資源,企業級環境會嚴格限定 origin 並配合其他安全措施。
* 的具體風險
-
數據暴露風險任意第三方站點都能通過 AJAX 獲取你的響應並在用户瀏覽器內讀取到響應數據(如果該請求不需要 cookie)。
-
無法與 credentials 一起用如果你的 API 依賴 Cookie / HTTP Auth(
credentials: 'include'),你不能設置*。瀏覽器會阻止讀取響應。這裏我説的
credentials: 'include'是fetch 請求選項裏的一個配置,用來控制 **是否在跨域請求中攜帶憑證(如 Cookie、HTTP Auth(Authorization 請求頭)、Client SSL 證書)**。當然,我們之前説過的,跨域訪問的話,服務端同時需要支持
Access-Control-Allow-Credentials: true。 -
緩存污染與中間件問題如果你對
Origin動態回顯而未設置Vary: Origin,中間緩存(CDN、代理)可能將一個 origin 的響應誤發給另一個 origin,造成安全/隱私漏洞。Origin是瀏覽器在 跨域請求 時自動攜帶的 請求頭。比如:Origin:https://frontend.com。Vary是服務器的 緩存控制響應頭。告訴緩存系統(如 CDN、瀏覽器緩存、負載均衡代理),當緩存這個響應時,需要 區分哪個請求頭不同 時生成不同的緩存版本。比如:Vary: Origin,不同 Origin 來訪問相同 URL,緩存系統要為他們分別緩存。
詳述 Origin 和 Very
鑑於很多同學在工作中,並沒有非常關注過這些配置,這裏再詳細講講。
先看常見的動態回顯代碼:res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
這表示 後端根據請求方的 Origin 動態設置 CORS 允許域名。
但是 **如果不設置 Vary: Origin**:
緩存(如 CDN 或代理服務器)可能把第一個請求的響應緩存下來。舉例:
- 用户 A(域名 a.com)訪問 → 緩存:
Access-Control-Allow-Origin:https://a.com - 用户 B(域名 b.com)訪問 → 本應得到:
Access-Control-Allow-Origin:https://b.com - 但由於緩存未區分,響應變成:
Access-Control-Allow-Origin:https://a.com
=> 跨域安全事故:b.com 拿到了 a.com 的權限配置緩存
正確做法:用白名單 + Vary
const allowList = ["https://a.com", "https://b.com"];
const origin = req.headers.origin;
if (allowList.includes(origin)) {
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Vary", "Origin"); // 關鍵
}
企業推薦做法
- 返回具體 origin(白名單)或動態回顯但要做嚴格白名單校驗。必要時返回
Access-Control-Allow-Credentials: true並確保Access-Control-Allow-Origin為允許的具體域名而非*。如果必須動態回顯Origin:務必設置 Vary: Origin。 - 對**公共靜態資源(圖標、CDN 上的公開腳本、字體)**:可以使用
*。例如圖片、CSS、JS(僅代表不包含敏感邏輯)通常可以*,便於 CDN 與第三方站點直接引用。 - 不要僅在客户端身份驗證與鑑權,客户端防“君子”不防“小人”,必須在服務器端強制檢查。不能把鑑權依賴於 CORS;CORS 只是瀏覽器的便利機制。對每個請求在後端都做 token/session 驗證和權限檢查。
- Cookie 策略與 SameSite(配合 CORS 使用),對跨站登錄場景優先使用
SameSite=None; Secure,並在前端使用fetch(..., credentials: 'include')。但前提是Access-Control-Allow-Origin不能為*。 - 使用反向代理,將前端請求同域發到 Nginx(配置同上述示例中的服務端配置),然後由 proxy 轉發到內部 API。這樣避免 CORS 的複雜性並能統一做鑑權、限流、審計。
詳述 SameSite=None; Secure
SameSite 是瀏覽器對 cookie 跨站發送 的限制策略,用來防止 CSRF 等跨站攻擊。SameSite 有三種值:
比如:Set-Cookie: session_id=xxxx; SameSite=None; Secure; HttpOnly; Path=/
Secure:cookie 只能在 HTTPS 的環境中發送,而且現代瀏覽器必須要求:SameSite=None➜ 必須同時帶 Secure 。HttpOnly:客户端 JS 無法讀 cookie,防止 XSS 。Path=/:這個 Cookie 在整個網站(所有路徑)都有效。比如,我們的網站是example.com,瀏覽器在example.com域下自動設置了 Cookie(Set-Cookie 已響應),之後只要訪問example.com這個域下的任意路徑,都會自動帶上它。- 比如:
Set-Cookie: token=abc123; Path=/user,訪問/user/xxx可以訪問,但是訪問/,/api等等的請求路徑就不會攜帶 token 。
- 比如:
寫到這裏,有些同學就想問了,這麼看來,cookie 挺好的呀,為什麼後面逐漸又出現了 jwt 這種會話控制方案呢?他們的區別是什麼呢?下一篇文章給你答案!