前言
原本想在寫 CORS 文件時候先介紹 same-origin policy 同源政策,但查資料後發現其歷史背景與技術內涵相當豐富,難以用簡短篇幅完整說明,所以後來就決定獨立一篇文章說明會比較清楚。
歷史
第一個網頁瀏覽器
1990 年,英國的電腦工程師 Tim Berners-Lee 在歐洲瑞士的核能研究單位 CERN 工作時,開發出第一套可實際運作的 Web 伺服器與瀏覽器系統之一。他將這個軟體稱為 WorldWideWeb (後改名為 Nexus)。這是一套針對 NeXT 系統電腦容易使用的圖形化介面瀏覽器,使原本分散的純文字文件得以首次透過公開網路互相連結。
JavaScript 誕生以前的 Web 世界
在那個 JavaScript 還沒誕生的時代,HTML 所有的表單驗證都要透過 CGI(Common Gateway Interface) 後端伺服器做資料驗證,在那個大部分使用者還在使用 28.8 kbit/s 撥接網路的時代,即使只是因為輸入錯誤,需等候後端伺服器驗證完後再被要求重新修正,也可能因反覆請求與回應而耗費數分鐘,使用體驗相當低落。
JavaScript 的誕生
西元 1995 年, Netscape 為改善瀏覽器互動體驗,決定開發一種可直接在瀏覽器中執行的輕量級腳本語言,專門來處理這類簡單的表單驗證。最初的名稱為 LiveScript ,由於當時 Netscape 與 Sun 合作密切,且新誕生的這門程式語言的語法外觀借鑑 Java ,語意與模型來自 Scheme / Self 。 Netscape 為了吸引媒體和開發者的關注,決定將名稱改為 JavaScript (當時 JAVA 1.0 幾乎同期發布),隨著 Netscape 2.0 beta 的發布,首次公開。
隨著時間的發展, JavaScript 漸漸開始多了操作 HTML DOM (Document Object Model) 、可以在瀏覽器存取 cookies ,以及發送網路請求等功能,也因此逐步成為 Web 核心技術之一。
初期的安全問題
在 JavaScript 剛出現的時候,覽器尚未建立完整的安全隔離模型,於是出現惡意網站讀取其他網站的 DOM 結構、 cookies ,來取得使用者資料,或是利用網站上銀行或是信用卡使用者已登入狀態進行未授權操作,竄改登入網站的畫面等等資訊安全事件。
此時 Web 世界首次面臨「多網站並存於同一瀏覽器環境」所帶來的安全挑戰,這些安全漏洞為網路犯罪提供了巨大的便利,並導致無數用戶的個資被盜,甚至財務損失。這些攻擊手法的出現,促使瀏覽器必須建立更清楚的安全邊界。
Same-Origin Policy 同源政策的誕生與演進
約在 1996 年,Netscape Navigator 隨 JavaScript 的成熟,逐步引入「同源限制」的安全模型,用以限制腳本僅能存取自身來源的 DOM 與敏感資料,此設計後來被稱為同源政策 Same-Origin Policy(SOP)。
最初的概念是保護不同網站對於 HTML DOM 存取,後來逐步擴展到全域的 JavaScript 敏感物件的存取,如: cookies 存取等等。
Microsoft 的 Internet Explorer 在 1997 年後開始實作類似限制, 1997–2000 期間 SOP 漸漸成為業界「瀏覽器默契」,並且在 2000 年代後,後來隨著 XMLHttpRequest 出現,SOP 的適用範圍進一步擴大,並在 2011 年 RFC 6454 正式定義 Origin 的概念,作為業界共識的安全基礎。
同源政策的基本原則
基本原則
瀏覽器判斷「是否同源」,看三個條件是否完全相同:
- 協定 (protocol) ex: http / https
- 網域(host) ex: example.com
- 連接埠 (port) ex: 80 / 443
以上三者必須相同,才會被瀏覽器視為同源
分級管控機制
現代 SOP 並不是所有的管控都是一刀切,全部功能採用相同標準,禁止跨源存取,實際上是依據 DOM 、 cookies 、 http request(AJAX) 分為以下存取標準
同源情況:
- DOM: ✅ 完全訪問
- Cookies: ✅自由讀寫
- AJAX 請求 (Fetch、XMLHttpRequest): ✅自由發送
- 資源載入(圖片、CSS、JS): ✅可載入
非同源:
- DOM: ❌ 禁止存取
- Cookies / LocalStorage: ❌ 禁止直接讀寫
- AJAX 請求 (Fetch、XMLHttpRequest): ⚠️ 可以發送請求,但無法讀取回應(除非 CORS)
- 資源載入(圖片、CSS、JS): ✅可載入,但無法讀取內容
CORS(Cross-origin resource sharing) 跨來源資源共用的出現
雖然 SOP 雖然可以避免跨網域攻擊,但是有時候網站部分服務或是資源是放在不同網域伺服器,或是需要引用其他網域的外部資源,此時就有了 CORS 解決方案的出現。
在 CORS 出現以前的解決方案
這裡只大約簡述其概念,因為那種早期解決方案並非本文主題。
JSONP(JSON with Padding) 在 CORS 出現以前的解決方案:
早期在 CORS 出現以前,開發者會透過 <script> 標籤來載入外部 JavaScript 來達成跨域資源請求。
<!-- 因為 XMLHttpRequest 無法跨域,所以前端會以類似載入外部 JavaScript 方式載入跨域資源 -->
<script>
function handleResponse(data) {
console.log(data);
}
</script>
<!-- 載入外部 JavaScript 來取得資源 -->
<script src="https://example.com/api?callback=handleResponse"></script>
伺服器返回
// 後端返回的不是 JSON 資料,而是一段 JavaScript 程式碼
handleResponse({ name: "Alice", age: 25 });
這種方式雖然能解決跨域問題,但是有容易被 XSS 攻擊利用,因此現代開發中較少使用。
CORS 的出現
CORS 細節是下個文章的主題,所以這裡只簡述其出現和標準化過程。
Tellme Networks 的馬特·奧什里(Matt Oshry)、布拉德·波特(Brad Porter)與麥克·波德爾(Michael Bodell)於 2004 年 3 月提案將跨來源支援加入 VoiceXML 2.1 ,以支援 VoiceXML 瀏覽器的跨來源資料請求。 W3C 認為這不應該限制在 VoiceXML 而是一般的機制,因此將提案移到另一份實作備忘錄,幾個主要的瀏覽器廠商透過 W3C 的 Web 應用程式工作小組正式的將該備忘錄改寫為 W3C 工作草案並以推動成為 W3C 推薦標準為目標。
參考
從零理解 Same-Origin Policy:瀏覽器安全的第一道防線














