同源策略

同源策略是一種關鍵的安全機制,它限制了一個 載入的文件或指令碼與另一個源的資源進行互動的方式。

它有助於隔離潛在的惡意文件,減少可能的攻擊向量。例如,它阻止網際網路上的惡意網站透過瀏覽器中的 JS 讀取第三方 Web 郵件服務(使用者已登入)或公司內網(攻擊者無法直接訪問,因為它沒有公共 IP 地址)的資料,並將該資料中繼給攻擊者。

源的定義

如果兩個 URL 的 協議(如果指定)和 主機 都相同,則它們具有相同的源。您可能會看到這被稱為“方案/主機/埠元組”或簡稱為“元組”。(“元組”是一組共同構成整體的項——這是雙/三/四/五/等……的通用形式。)

下表給出了與 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/page.html 失敗 協議不同
http://store.company.com:81/dir/page.html 失敗 埠不同(http:// 預設埠為 80)
http://news.company.com/dir/page.html 失敗 主機不同

繼承的源

從具有 about:blankjavascript: URL 的頁面執行的指令碼會繼承包含該 URL 的文件的源,因為這些型別的 URL 不包含有關源伺服器的資訊。

例如,about:blank 通常用作新的、空白的彈出視窗的 URL,父指令碼會將內容寫入其中(例如,透過 Window.open() 機制)。如果此彈出視窗也包含 JavaScript,則該指令碼將繼承與其建立者相同的源。

data: URL 會獲得一個新的、空的、安全上下文。

檔案源

現代瀏覽器通常將使用 file:/// 方案載入的檔案的源視為不透明源。這意味著,如果一個檔案包含同一資料夾中的其他檔案(例如),它們不會被假定來自同一源,並可能觸發 CORS 錯誤。

請注意,URL 規範指出檔案的源是實現定義的,並且某些瀏覽器可能會將同一目錄或子目錄中的檔案視為同源,儘管這會產生安全隱患

更改源

警告:此處描述的方法(使用 document.domain 設定器)已棄用,因為它會削弱同源策略提供的安全保護,並使瀏覽器中的源模型複雜化,導致互操作性問題和安全漏洞。

頁面可以更改其自身的源,但有一些限制。指令碼可以將 document.domain 的值設定為其當前域或其當前域的超域。如果設定為當前域的超域,則使用較短的超域進行同源檢查。

例如,假設來自 http://store.company.com/dir/other.html 文件的指令碼執行了以下操作

js
document.domain = "company.com";

之後,該頁面可以透過同源檢查與 http://company.com/dir/page.html 進行比較(假設 http://company.com/dir/page.html 將其 document.domain 設定為 "company.com" 以表示它允許這樣做——請參閱 document.domain 以瞭解更多資訊)。但是,company.com不能document.domain 設定為 othercompany.com,因為它不是 company.com 的超域。

埠號由瀏覽器單獨檢查。任何對 document.domain 的呼叫,包括 document.domain = document.domain,都會導致埠號被覆蓋為 null。因此,僅在第一個中設定 document.domain = "company.com" 不能使 company.com:8080company.com 通訊。必須同時在兩者中設定,以便它們的埠號都為 null

該機制存在一些限制。例如,如果文件位於沙盒化的 <iframe> 中,它將丟擲 SecurityError DOMException,並且以這種方式更改源不會影響許多 Web API(例如 localStorageindexedDBBroadcastChannelSharedWorker)使用的源檢查。更全面的故障案例列表可以在 Document.domain > 故障 中找到。

注意:當使用 document.domain 允許子域訪問其父域時,您需要在父域和子域中都將 document.domain 設定為相同的值。即使這樣做是為了將父域設定回其原始值,也是必需的。否則可能會導致許可權錯誤。

跨域網路訪問

同源策略控制不同源之間的互動,例如當您使用 fetch()<img> 元素時。這些互動通常分為三類

  • 跨域寫入通常是允許的。例如連結、重定向和表單提交。某些 HTTP 請求需要 預檢
  • 跨域嵌入通常是允許的。(下面列出了示例。)
  • 跨域讀取通常是不允許的,但讀取訪問經常透過嵌入洩露。例如,您可以讀取嵌入影像的尺寸、嵌入指令碼的操作或嵌入資源的可用性

以下是一些可能被跨域嵌入的資源示例

  • 帶有 <script src="…"></script> 的 JavaScript。語法錯誤的錯誤詳細資訊僅對同源指令碼可用。
  • 使用 <link rel="stylesheet" href="…"> 應用的 CSS。由於 CSS 的寬鬆語法規則,跨域 CSS 需要正確的 Content-Type 標頭。如果是一個 MIME 型別不正確且資源不以有效 CSS 構造開頭的跨域載入,瀏覽器會阻止樣式表載入。
  • <img> 顯示的影像。
  • <video><audio> 播放的媒體。
  • 使用 <object><embed> 嵌入的外部資源。
  • @font-face 應用的字型。某些瀏覽器允許跨域字型,其他瀏覽器則要求同源。
  • <iframe> 嵌入的任何內容。站點可以使用 X-Frame-Options 標頭來防止跨域幀。

如何允許跨域訪問

使用 CORS 允許跨域訪問。CORS 是 HTTP 的一部分,它允許伺服器指定瀏覽器應允許載入內容的任何其他主機。

如何阻止跨域訪問

  • 為了防止跨域寫入,請在請求中檢查一個不可猜測的令牌——稱為跨站請求偽造 (CSRF) 令牌。您必須阻止對需要此令牌的頁面的跨域讀取。
  • 為了阻止對資源的跨域讀取,請確保它無法被嵌入。通常需要阻止嵌入,因為嵌入資源總是會洩露有關該資源的一些資訊。
  • 為了阻止跨域嵌入,請確保您的資源不能被解釋為上面列出的可嵌入格式之一。瀏覽器可能不會尊重 Content-Type 標頭。例如,如果您將 <script> 標籤指向一個 HTML 文件,瀏覽器將嘗試將 HTML 解析為 JavaScript。當您的資源不是您網站的入口點時,您還可以使用 CSRF 令牌來防止嵌入。

跨域指令碼 API 訪問

JavaScript API,如 iframe.contentWindowwindow.parentwindow.openwindow.opener 允許文件直接相互引用。當兩個文件不具有相同的源時,這些引用對 WindowLocation 物件的訪問非常有限,如下一個兩個部分所述。

要實現不同源之間的文件通訊,請使用 window.postMessage

規範:HTML Living Standard § Cross-origin objects

Window

以下對這些 Window 屬性的跨域訪問是允許的

方法
window.blur
window.close
window.focus
window.postMessage
屬性
window.closed 只讀。
window.frames 只讀。
window.length 只讀。
window.location 讀/寫。
window.opener 只讀。
window.parent 只讀。
window.self 只讀。
window.top 只讀。
window.window 只讀。

某些瀏覽器允許訪問比上述更多的屬性。

Location

以下對 Location 屬性的跨域訪問是允許的

方法
location.replace
屬性
location.href 只寫。

某些瀏覽器允許訪問比上述更多的屬性。

跨域資料儲存訪問

對瀏覽器中儲存的資料(如 Web StorageIndexedDB)的訪問按源進行分隔。每個源都有自己的獨立儲存,一個源中的 JavaScript 無法讀取或寫入屬於另一個源的儲存。

Cookie 使用獨立的源定義。頁面可以為其自己的域或任何父域設定 cookie,只要父域不是公共字尾。Firefox 和 Chrome 使用 公共字尾列表來確定域是否為公共字尾。設定 cookie 時,您可以使用 DomainPathSecureHttpOnly 標誌來限制其可用性。讀取 cookie 時,您看不到它是從哪裡設定的。即使您只使用安全的 https 連線,您看到的任何 cookie 都可能是在不安全的連線上設定的。

另見