同源策略是web安全策略中的一種,非常重要。
同源策略明確規定:不同域的客戶端在沒有明確授權的情況下,不能讀寫對方的資源。
簡單說來就是web瀏覽器允許第一個頁面的腳本訪問訪問第二個頁面的數據,但是也只有在兩個頁面有相同的源的時候,如果不同源則需要授權。源:URI(統一資源標識符)、主機名、端口號組合而成的。這個策略可以阻止一個頁面上惡意腳本通過頁面DOM對象獲得訪問另一個頁面的敏感信息的權限。
歷史
同源策略的概念要追溯到1995年的網景瀏覽器。同源策略作為一個重要的安全基石,所有的現代瀏覽器都在一定程度上實現了同源策略。同源策略雖然不是一個明確規范,但是經常為某些web技術(例如Microsoft Silverlight,Adobe Flash,Adobe Acrobat)或者某些機制(例如XMLHttpRequest)擴展定義大致兼容的安全邊界。
源決定規則
RFC6454中有定義URI源的算法定義。對于絕對的URIs,源就是{協議,域名,端口}定義的。只有這些值完全一樣才認為兩個資源是同源的。如http://www.example.com:80/dir/page.html
http:// 協議
www.example.com 域名
:80 端口號
舉個栗子:比較表格中的URL與”http://www.example.com/dir/page.html“是否同域。
對比URL | 結果 | 結果分析 |
---|---|---|
http://www.example.com/dir/page2.html | 同源 | 相同的協議、域名、端口號 |
http://www.example.com/dir2/other.html | 同源 | 相同的協議、域名、端口號 |
http://username:password@www.example.com/dir2/other.html | 同源 | 相同的協議、域名、端口號 |
http://www.example.com:81/dir/other.html | 不同源 | 相同的協議、域名;不同的端口號 |
https://www.example.com/dir/other.html | 不同源 | 相同的域名、端口號;不同的協議 |
http://en.example.com/dir/other.html | 不同源 | 相同的協議、端口號;不同的域名 |
http://example.com/dir/other.html | 不同源 | 不同的域名(需要精確匹配) |
http://v2.www.example.com/dir/other.html | 不同源 | 不同的域名(需要精確匹配) |
http://www.example.com:80/dir/other.html | 看情況 | 端口明確,需要依賴于瀏覽器的實現 |
不像其他瀏覽器,IE在計算源的時候沒有包括端口。
限制的范圍
隨著互聯網的發展,”同源政策”越來越嚴格。目前,如果非同源,共有三種行為受到限制。
- Cookie、LocalStorage 和 IndexDB 無法讀取。
- DOM 無法獲得。
AJAX 請求不能發送。
雖然這些限制是必要的,但是有時很不方便,合理的用途也受到影響。
授權
默認情況下訪問一個站點是不允許跨越的,只有目標站點(比如:http://www.example.com/dir/page.html)明確返回HTTP響應頭Access-Control-Allow-Origin: http://www.evil.com 那么www.evil.com站點上的客戶端腳本就有權通過AJAX技術對www.foo.com上的數據進行讀寫操作。
讀寫權限
Web上的資源有很多,有的只有讀權限,有的同時擁有讀和寫的權限。比如:HTTP請求頭里的Referer(表示請求來源)只可讀,而document.cookie則具備讀寫權限。這樣的區分也是為了安全上的考慮。
安全的考量
有這種限制的主要原因是因為如果沒有同源策略將導致安全風險。假設用戶在訪問銀行網站,并且沒有登出。然后他又去了任意的其他網站,剛好這個網站有惡意的js代碼,在后臺請求銀行網站的信息。因為用戶目前仍然是銀行站點的登陸狀態,那么惡意代碼就可以在銀行站點做任意事情。例如,獲取你的最近交易記錄,創建一個新的交易等等。因為瀏覽器可以發送接收銀行站點的session cookies,在銀行站點域上。訪問惡意站點的用戶希望他訪問的站點沒有權限訪問銀行站點的cookie。當然確實是這樣的,js不能直接獲取銀行站點的session cookie,但是他仍然可以向銀行站點發送接收附帶銀行站點session cookie的請求,本質上就像一個正常用戶訪問銀行站點一樣。關于發送的新交易,甚至銀行站點的CSRF(跨站請求偽造)防護都無能無力,因為腳本可以輕易的實現正常用戶一樣的行為。所以如果你需要session或者需要登陸時,所有網站都面臨這個問題。如果上例中的銀行站點只提供公開數據,你就不能觸發任意東西,這樣的就不會有危險了,這些就是同源策略防護的。當然,如果兩個站點是同一個組織的或者彼此互相信任,那么就沒有這種危險了。
規避同源策略
在某些情況下同源策略太嚴格了,給擁有多個子域的大型網站帶來問題。下面簡要介紹解決這種問題的技術:
document.domain屬性
如果兩個window或者frames包含的腳本可以把domain設置成一樣的值,那么就可以規避同源策略,每個window之間可以互相溝通。例如,orders.example.com
下頁面的腳本和catalog.example.com
下頁面的腳本可以設置他們的document.domain
屬性為example.com
,從而讓這兩個站點下面的文檔看起來像在同源下,然后就可以讓每個文檔讀取另一個文檔的屬性。
跨域資源共享
這種方式即上文所說的授權,使用了一個新的Origin
請求頭和一個新的Access-Control-Allow-Origin
響應頭擴展了HTTP。允許服務端設置Access-Control-Allow-Origin
頭標識哪些站點可以請求文件,或者設置Access-Control-Allow-Origin
頭為”*”,允許任意站點訪問文件。
跨文檔通信
這種方式允許一個頁面的腳本發送文本信息到另一個頁面的腳本中,不管腳本是否跨域。在一個window對象上調用postMessage()會異步的觸發window上的onmessage事件,然后觸發定義好的事件處理方法。一個頁面上的腳本仍然不能直接訪問另外一個頁面上的方法或者變量,但是他們可以安全的通過消息傳遞技術交流。
JSONP
JOSNP允許頁面接受另一個域的JSON數據,通過在頁面增加一個可以從其它域加載帶有回調的JSON響應的<script>
標簽。
WebSocket
現代瀏覽器允許腳本直連一個WebSocket地址而不管同源策略。然而,使用WebSocket URI的時候,在請求中插入Origin頭就可以標識腳本請求的源。為了確保跨站安全,