跨域-理論篇

飛機飛過天空發表於2021-01-11

瀏覽器限制一個源的文件(document)或它載入的指令碼(script)如何與另一個源的資源進行互動。它能幫助阻隔惡意文件,減少可能被攻擊的媒介。但是這樣也造成問題:跨域。在前後端分離的專案中,前端傳送請求給後臺伺服器,後臺伺服器接收到請求,處理完響應給前端。但是由於瀏覽器的同源策略(Same-origin policy),該響應被攔截了。

什麼是同源

要了解同源,先來看看 URL 是怎麼組成的:

protocol://[host]:[port][path][?query][anchor]

e.g.

http://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument

其中:

  • protocol 為協議,這裡為 http
  • host 為域名,這裡為 www.example.com。其中 example.com 為主域名,www.example.com 為子域名。
  • port 為埠,這裡為 80
  • path 為資源路徑,這裡為 /path/to/myfile.html
  • query 為查詢字串,這裡為 ?key1=value1&key2=value2
  • anchor 為描點,這裡為 #SomewhereInTheDocument

在 URL 中,只要 protocol, hostpath 相同,則認定為同源。

例如,一個 URL 為 http://store.company.com/dir/page.html 下面列出對該源的比較:

URL 結果 原因
http://store.company.com/dir2/other.html 同源 只有路徑不同
http://store.company.com/dir/inner/another.html 同源 只有路徑不同
https://store.company.com/secure.html 失敗 協議不同
http://store.company.com:81/dir/etc.html 失敗 埠不同 ( http:// 預設埠是80)
http://news.company.com/dir/other.html 失敗 主機不同
http://company.com/dir/other.html 失敗 主機不同,storecompany.com 的一個子域名。

同源策略

瀏覽器認為網際網路是不可信的,於是它引入了同源策略,拒絕非同源的請求。

瀏覽器共限制三種行為:

  • Cookie、LocalStorage 和 IndexDB 只能同源讀取。

    其中,Cookie 的同源策略有點不同。可以為父域(e.g. example.com)設定 Cookie,那樣其子域(e.g. www.example.com, abc.example.com)都共享該 Cookie。當然也可以只為子域設定 Cookie,這樣該 Cookie 只能子域訪問。

    這個比較好理解,自己源的資料只能自己讀取。要是別人都能讀取,那資訊不久全部洩露了。

  • DOM 只能同源獲得。

    也就是兩個不同源的頁面之間無法對方的 DOM 元素。如果該限制取消了,就會造成安全隱患。最經典就是 iframe 的例子:

    你在某處開啟了這個網站 https://www.bilibil.com/,這個網站看起來和嗶哩嗶哩沒有任何區別,但是仔細觀察它並不是官方網站。黑客使用 iframe 載入真正的嗶哩嗶哩網站,並將 iframe 佈局調整全屏,看起來就和真正的嗶哩嗶哩沒有任何區別。而你也沒有察覺這樣的問題,於是你就登入你的賬號。如果沒有 DOM 同源策略,黑客就可以從 https://www.bilibil.com/ 網站直接抓取輸入框的 DOM,從而拿到你的賬號和密碼。

  • XMLHttpRequest 只能同源請求。

    XMLHttpRequest 也就是我們常說的 AJAX。AJAX 的同源可以免受瀏覽器遭受到 CSRF 攻擊(並不能抵擋全部的 CSRF 攻擊)。

這裡主要討論同源策略中不同源之間的請求、互動限制。

同源策略中對不同源之間的互動限制,比如主要分為三類:

  • 跨域寫操作(Cross-origin writes) 一般是被允許的例如,連結(links),重定向以及表單提交。
  • 跨域資源嵌入(Cross-origin embedding) 一般是被允許。例如,一系列可以通過 src 引入資源的標籤:scriptimgvideoaudio 等;通過 @font-face 引入的字型;通過 iframe 載入的任何資源。
  • 跨域讀操作(Cross-origin reads) 一般是不被允許的。例如,AJAX。

解決方案

解決跨域,一般就是解決不同源之間可以互相請求 AJAX,不同源之間可以互相獲取 DOM 節點。

AJAX 解決方案

在說明解決方案之前,來看看瀏覽器是如何阻止 AJAX 跨域的。

  1. 我們在當前網站中請求一個非同域的 API。
  2. 瀏覽器就會給該伺服器傳送請求。
  3. 伺服器接收到請求後,處理過後成功響應。
  4. 瀏覽器接受到這個響應,發現該請求為跨域響應,於是拒絕了該響應。

從上面得出結論,瀏覽器並不是不能傳送跨域請求,而是拒絕了跨域響應。

這也是我們在請求非同源網站時,瀏覽器會返回 net::ERR_FAILED 並告訴我們並沒有設定 Access-Control-Allow-Origin 這個頭資訊。

解決方案一:CORS

經過多年的發展,前後端分離已成為業界標杆。既然是前後端分離就要處理跨域問題。

我們知道,在伺服器返回響應給瀏覽器後,瀏覽器是拒絕了該響應。所以 W3C 就推出一個標準:CORS。它是通過設定一系列的響應頭來解決跨域問題的。

最重要的響應頭當然是 Access-Control-Allow-Origin,它可以設定成 * 代表允許所有網站的請求跨域。也可以設定成 URI,只允許指定的網站請求跨域。

這裡列舉常用的響應頭,更詳細的內容可以檢視 MDN 的文件

  • Access-Control-Allow-Origin 請求資源能共享給那些域
  • Access-Control-Allow-Credentials 是否允許傳送 Cookie
  • Access-Control-Allow-Methods 對預請求的響應中,指示實際的請求中可以使用哪些 HTTP 頭。
  • Access-Control-Max-Age 預請求結果能被快取多久,單位為秒。-1 表示禁用快取。

因為是響應,所以 CORS 的解決方案在後端進行。

對了,CORS 分為 簡單請求非簡單請求 這裡不過多贅述,詳情請看文件

解決方案二:代理伺服器

我們知道同源策略是瀏覽器上的限制,伺服器之間是沒有跨域這個說法的。

所以可以加個中間層來解決這個問題:

  1. 瀏覽器的請求發到同源代理伺服器
  2. 同源代理伺服器收到請求後進行轉發,轉發到不同源的伺服器。以此進行資料互動。
  3. 資料互動之後,先傳送到同源代理伺服器,再轉發到瀏覽器。

整個過程瀏覽器只和同源代理伺服器交流,故解決跨域。

解決方案三:JSONP

JSONP 可以堪稱是跨域解決的奇淫技巧,因為它是利用 <script> 元素的「漏洞」來實現的。

從上面我們可以知道,<script> 標籤是可以引用其他域的 js 指令碼,該 js 指令碼載入成功後會觸發回撥函式,該回撥函式裡面就有我們想要的資料。

使用 JSONP 的好處為瀏覽器相容性好,但它只能支援 GET 請求。

解決方案四:WebSocket

WebSocket 和 HTTP 一樣都是應用層的協議,與 HTTP 不一樣的是,它並沒有同源策略。只要伺服器支援該協議,就可以實現跨域通訊。

獲取 DOM 解決方案

我們知道 iframewindow.open 開啟的視窗,他們與父視窗之前無法通訊。父視窗無法獲取子視窗的 DOM,反之亦然。

document.domain

通過 document.domain 這個屬性值,如果兩個視窗的一級域名相同,則可以施行視窗之間的通訊。同樣可以通訊的還有 Cookie 值。

片段識別符號(fragment identifier)

片段識別符號指的是 URL 後面 # 號後面的部分,也成為錨(anchor)。

如果只是改變片段識別符號的內容,頁面就不會重新重新整理,從而實現跨域。

window.name

windows.name 是瀏覽器視窗的一個屬性。該屬性,無論是否同源,只要在同一個視窗裡,前一個網頁設定了這個屬性,後一個網頁可以讀取它。

window.postMessage

上面兩種方法都是利用的漏洞,HTML 5 引入跨文件通訊 API(Cross-document messaging)來解決這個問題。

這個 API 為 window 物件新增了一個 window.postMessage 方法,允許跨視窗通訊,不論這兩個視窗是否同源。

它可以解決一下方面的問題:

  • 頁面和其開啟的新視窗的資料傳遞
  • 多視窗之間訊息傳遞
  • 頁面與巢狀的iframe訊息傳遞
  • 上面三個場景的跨域資料傳遞

window.postMessage 這個方法非常強大,詳情請參考 文件

參考連結

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章