理解 CORS 與同源政策:從 Zoom 漏洞中學習的教訓

核心要點:CORS 不是伺服器的安全工具

跨來源資源共用 (Cross-Origin Resource Sharing, CORS) 並不是一種防止請求到達伺服器的機制;相反地,它是一種瀏覽器端的機制,允許伺服器放寬同源政策 (Same-Origin Policy, SOP) 的限制,以便前端應用程式可以讀取回應。誤解這一區別經常導致開發者實施「黑客手段」來繞過 CORS,或者錯誤地認為 CORS 標頭可以保護其後端免受未經授權的請求。

個案研究:Zoom Localhost 漏洞

在 2019 年,Zoom 發現了一個漏洞,該應用程式在 localhost:19421 上執行一個 Web 伺服器,以允許 Zoom 網站觸發原生應用程式。為了在向此本地伺服器發送 AJAX 請求時繞過 CORS 限制,Zoom 實施了一種「圖片黑客手段 (image hack)」,將數據編碼到圖片檔案的尺寸中。

為什麼圖片黑客手段會造成安全漏洞

由於瀏覽器通常允許跨來源載入圖片而不會受到與 AJAX 請求相同的限制,這種繞過方式有效地禁用了同源政策 (SOP) 提供的保護。因此,網際網路上的任何網站——而不僅僅是 zoom.us——都可以向本地 Zoom 伺服器發送請求,並透過圖片尺寸讀取回應,這可能導致原生用戶端執行未經授權的操作。

正確的實施方式

為了確保此功能的安全性,Zoom 應該在 localhost 伺服器上實施 REST API,並將 Access-Control-Allow-Origin 標頭特別設置為 https://zoom.us。這將允許官方 Zoom 網站讀取回應,同時防止其他惡意網站執行此操作。此外,使用阻擋 iframe 渲染的內容安全政策 (Content Security Policy, CSP) 可以防止 Zoom 網站被嵌入到惡意的 iframe 中以觸發背景動作。

SOP 與 CORS:釐清混淆點

開發者的大部分混淆源於未能區分同源政策 (SOP) 與跨來源資源共用 (CORS)。

同源政策 (SOP)

SOP 是主要的安全性邊界。它防止來自一個來源 (協定、主機與連接埠) 的文件讀取屬於另一個來源的數據。例如,SOP 防止 malicious-site.com 在您登入時從 gmail.com 獲取您的私人郵件。

跨來源資源共用 (CORS)

CORS 是一種用來「放寬」SOP 的機制。它允許伺服器明確地告訴瀏覽器:「我信任這個特定的來源,因此你可以讓在那裡執行的 JavaScript 讀取此請求的回應。」

關鍵技術區別

  • 寫入與讀取: SOP 通常阻擋的是跨來源回應的「讀取」,而不是請求的「發送」。一個請求可以被發送並由伺服器處理(例如,一個 POST 請求),但除非存在 CORS 標頭,否則瀏覽器會阻擋 JavaScript 讀取結果。
  • 執行點: CORS 完全由瀏覽器強制執行。對於透過非瀏覽器用戶端(如 curl 或 Postman)訪問的伺服器,CORS 提供零保護,因為這些用戶端會完全忽略 CORS 標頭。

為什麼開發者會對 CORS 感到困惑

開發者之間的技術討論突顯了幾個原因,說明為什麼 CORS 仍然是一個持續的混淆點:

  • 反向的安全模型: 大多數安全模型假設伺服器是訪問權限的仲裁者。在 CORS 中,瀏覽器才是仲裁者,保護伺服器與用戶端免受惡意代碼的侵害。
  • 不可見的威脅模型: CORS 所防止的威脅在開發期間通常是假設性的。開發者只有在嘗試讓代碼工作時遇到 CORS 作為一種「困擾」的錯誤訊息,這導致他們使用不安全的預設值(例如 Access-Control-Allow-Origin: *)。
  • 糟糕的錯誤訊息: 出於安全原因,瀏覽器對於 CORS 失敗的錯誤訊息通常刻意模糊,這使得它們難以除錯與區分其他網路失敗。

"CORS 本質上與 [安全模型的預設形態] 非常不同... 它需要短暫但仔細的閱讀才能理解這個概念——而那些一心只想著完成應用程式代碼編寫的開發者可能不希望因此放慢速度。"

避免 CORS 問題的最佳實踐

  1. 使用反向代理: 將後端託管在與前端相同的來源上(例如,透過反向代理),可以透過遵循原始的 SOP 來完全消除對 CORS 的需求。
  2. 避免 Localhost 黑客手段: 切勿使用圖片或腳本標籤來繞過 CORS 以進行特權操作。如果需要本地伺服器,請實施正確的 CORS 標頭,並在伺服器端驗證請求的來源。
  3. 像攻擊者一樣思考: 意識到任何到達您伺服器的請求都可能是惡意的。不要依賴 CORS 來防止請求被執行;請為每個請求使用正確的身分驗證與授權令牌。

Sources