瀏覽器的同源策略
同源策略限制從一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用于隔離潛在惡意文件的關鍵的安全機制。
一個源的定義
如果協議,端口(如果指定了一個)和域名對于兩個頁面是相同的,則兩個頁面具有相同的源。
下表給出了相對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 失敗 不同協議 ( https和http )
http://store.company.com:81/dir/etc.html 失敗 不同端口 ( 81和80)
http://news.company.com/dir/other.html 失敗 不同域名 ( news和store )
源的繼承
來自about:blank和javascript:URL的內容從加載URL的文檔繼承原始文檔,因為URL本身不提供任何有關原點的信息。 data:URLs獲得一個新的,空的安全上下文。
IE 例外
當涉及到同源策略時,Internet Explorer有兩個主要的例外
授信范圍(Trust Zones):兩個相互之間高度互信的域名,如公司域名(corporate domains),不遵守同源策略的限制。
端口:IE未將端口號加入到同源策略的組成部分之中,因此 http://company.com:81/index.html 和http://company.com/index.html 屬于同源并且不受任何限制。
這些例外是非標準的,其它瀏覽器也未做出支持,但會助于開發基于window RT IE的應用程序。
源的更改
頁面可能會更改其自己的來源,但有一些限制。腳本可以將
document.domain
的值設置為其當前域或其當前域的超級域。如果將其設置為其當前域的超級域,則較短的域將用于后續原始檢查。例如,假設文檔中的一個腳本在 http://store.company.com/dir/other.html 執行以下語句:
document.domain = "company.com";
這條語句執行之后,頁面將會成功地通過對
http://company.com/dir/page.html
的同源檢測。而同理,company.com
不能設置
document.domain
為
othercompany.com
.
瀏覽器單獨保存端口號。任何的賦值操作,包括document.domain = document.domain都會以null值覆蓋掉原來的端口號。因此
company.com:8080頁面的腳本不能僅通過設置document.domain = "company.com"就能與company.com通信。賦值時必須帶上端口號,以確保端口號不會為null。
注意:使用document.domain允許子域安全訪問其父域時,您需要設置document.domain在父域和子域中具有相同的值。這是必要的,即使這樣做只是將父域設置回其原始值。否則可能會導致權限錯誤。
跨源網絡訪問
同源策略控制了不同源之間的交互,例如在使用XMLHttpRequest
是一個API, 它為客戶端提供了在客戶端和服務器之間傳輸數據的功能。它提供了一個通過 URL 來獲取數據的簡單方式,并且不會使整個頁面刷新。這使得網頁只更新一部分頁面而不會打擾到用戶。XMLHttpRequest 在 AJAX 中被大量使用。")
或
<img>
標簽時則會受到同源策略的約束。交互通常分為三類:
通常允許進行跨域寫操作(Cross-origin writes)。例如鏈接(links),重定向以及表單提交。特定少數的HTTP請求需要添加
preflight
通常允許跨域資源嵌入(Cross-origin embedding)。之后下面會舉例說明。
通常不允許跨域讀操作(Cross-origin reads)。但常可以通過內嵌資源來巧妙的進行讀取訪問。例如可以讀取嵌入圖片的高度和寬度,調用內嵌腳本的方法,或
availability of an embedded resource
。
如何允許跨源訪問
使用
允許跨源訪問。
如何阻止跨源訪問
-
阻止跨域寫操作,只要檢測請求中的一個不可測的標記(CSRF token)即可,這個標記被稱為
Cross-Site Request Forgery (CSRF)
標記。必須使用這個標記來阻止頁面的跨站讀操作。
阻止資源的跨站讀取,需要保證該資源是不可嵌入的。阻止嵌入行為是必須的,因為嵌入資源通常向其暴露信息。
-
阻止跨站嵌入,確保你得資源不能是以上列出的可嵌入資源格式。多數情況下瀏覽器都不會遵守Conten-Type消息頭。例如,如果你在<script>標簽中嵌入HTML文檔,瀏覽器仍將HTML解析為Javascript。
When your resource is not an entry point to your site, you can also use a CSRF token to prevent embedding.
跨源腳本API訪問
Javascript的APIs中,如
[iframe.contentWindow
],[window.parent
][window.open
]根據指定的參數,將一個資源加載到一個新的瀏覽上下文(如一個窗口)或一個已經存在的瀏覽上下文中。")和[window.opener
]返回打開當前窗口的那個窗口的引用.")
允許文檔間直接相互引用。當兩個文檔的源不同時,這些引用方式將對[Window]和 [Location]對象的訪問添加限制。可以使用[window.postMessage
] window.postMessage() 方法可以安全地實現跨源通信。通常,對于兩個不同頁面的腳本,只有當執行它們的頁面位于具有相同的協議(通常為https),端口號(443為https的默認值),以及主機 (兩個頁面的模數 Document.domain設置為相同的值) 時,這兩個腳本才能相互通信。window.postMessage() 方法提供了一種受控機制來規避此限制,只要正確的使用,這種方法就很安全。")
作為替代方案,提供跨域文檔間的通訊。
位置
Specification:
http://www.whatwg.org/specs/web-apps/current-work/multipage/browsers.html#security-location.
允許以下對位置屬性的跨源訪問:
Methods |
---|
location.replace |
Attributes | |
---|---|
URLUtils.href |
只寫. |
某些瀏覽器允許訪問比規范允許的更多屬性
跨源數據存儲訪問
存儲在瀏覽器中的數據,如localStorage和IndexedDB,以源進行分割。每個源都擁有自己單獨的存儲空間,一個源中的Javascript腳本不能對屬于其它源的數據進行讀寫操作。
window.name屬性可以用來臨時存儲數據,可以跨域訪問。
Cookies使用不同的源定義方式。一個頁面可以為本域和任何父域設置cookie,只要是父域不是公共后綴(public suffix)即可。Firefox和Chrome使用Public Suffix List決定一個域是否是一個公共后綴(public suffix)。不管使用哪個協議(HTTP/HTTPS)或端口號,瀏覽器都允許給定的域以及其任何子域名(sub-domains)來訪問cookie。設置cookie時,你可以使用Domain,Path,Secure,和Http-Only標記來限定其訪問性。讀取cookie時,不會知曉它的出處。盡管使用安全的https連接,任何可見的cookie都是使用不安全的連接設置的。