同源策略淺析
- 2020 年 1 月 2 日
- 筆記
為什麼使用同源策略?
一個重要原因就是對cookie的保護,cookie 中存着sessionID 。如果已經登錄網站,同時又去了任意其他網站,該網站有惡意JS代碼。如果沒有同源策略,那麼這個網站就能通過js 訪問document.cookie 得到用戶關於的各個網站的sessionID。其中可能有銀行網站,通過已經建立好的session連接進行攻擊,這裡有一個專有名詞,CSRF,還有需要注意的是同源策略無法完全防禦CSRF,這裡需要服務端配合。
什麼是同源策略?
URL由協議、域名、端口和路徑組成,如果兩個URL的協議、域名和端口相同,則表示他們是同源的。同源策略是瀏覽器上為安全性考慮實施的非常重要的安全策略。限制來自不同源的"document",對當前"document"讀取或設置某些屬性。在瀏覽器中,<script>、<img>、<link>、<frame>等標籤都可加載跨域資源,而不受同源策略限制,這帶"src"屬性的標籤加載時,實際上是由瀏覽器發起一次GET請求,不同於XMLHttpRequest,他們通過src屬性加載的資源。但瀏覽器限制了JavaScript的權限,使其不能讀、寫其中返回的內容。
跨域請求的安全基礎是,JavaScript無法修改請求對象的http頭部。如果XMLHttpRequest能夠跨域請求資源,可能導致敏感信息泄露,比如CSRF的token信息
受同源策略限制的有哪些?
DOM、Cookie、XMLHttpRequest,還有一些第三方插件Flash、Java Applet、Sliverlight、Google Gears等都有自己的控制策略。
如何規避同源策略,即跨域請求?
document.domain屬性
如果兩個window或者frames包含的腳本可以把domain設置成一樣的值,那麼就可以規避同源策略,每個window之間可以互相溝通。例如,orders.example.com下頁面的腳本和catalog.example.com下頁面的腳本可以設置他們的document.domain屬性為example.com,從而讓這兩個站點下面的文檔看起來像在同源下,然後就可以讓每個文檔讀取另一個文檔的屬性。這種方式也不是一直都有用,因為端口號是在內部保存的,有可能被保存成null。換句話說,example.com的端口號80,在我們更新document.domain屬性的時候可能會變成null。為null的端口可能不被認為是80,這主要依賴瀏覽器實現。
跨域資源共享(CORS)
Cross-origin Resource Sharing跨資源共享,使用自定義的HTTP頭部讓瀏覽器與服務器溝通,從而決定請求和響應是否成功。這種方式使用了一個新的Origin請求頭和一個新的Access-Control-Allow-Origin響應頭擴展了HTTP。允許服務端設置Access-Control-Allow-Origin頭標識哪些站點可以請求文件,或者設置Access-Control-Allow-Origin頭為"*",允許任意站點訪問文件。
瀏覽器,例如Firefox3.5,Safari4,IE10使用這個頭允許跨域HTTP請求。
服務器端在HTTP的響應頭中加入(頁面層次的控制模式):
Access-Control-Allow-Origin: example.com
Access-Control-Request-Method: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization, Accept, Range, Origin
Access-Control-Expose-Headers: Content-Range
Access-Control-Max-Age: 3600
多個域名之間用逗號分隔,表示對所示域名提供跨域訪問權限。"*"表示允許所有域名的跨域訪問
客戶端可以有兩種行為:
1. 發送OPTIONS請求,請求Access-Control信息。如果自己的域名在允許的訪問列表中,則發送真正的請求,否則放棄請求發送。
2. 直接發送請求,然後檢查response的Access-Control信息,如果自己的域名在允許的訪問列表中,則讀取response body,否則放棄。本質上服務端的response內容已經到達本地,JavaScript決定是否要去讀取。
跨文檔通信(window.postMessage方法)
這種方式允許一個頁面的腳本發送文本信息到另一個頁面的腳本中,不管腳本是否跨域。基本上,它就像是跨域的AJAX,但不是瀏覽器跟服務器之間交互,而是在兩個客戶端之間通信。在一個window對象上調用postMessage()會異步的觸發window上的onmessage事件,然後觸發定義好的事件處理方法。一個頁面上的腳本仍然不能直接訪問另外一個頁面上的方法或者變量,但是他們可以安全的通過消息傳遞技術交流。
允許程序員跨域在兩個窗口/frames間發送數據信息。
窗口A: 發送窗口使用postMessage發送數據 window.postMessage(msg,urlOfB);
窗口B: 接收端,監聽「message」事件
window.onmessage(event){
var data=event.data;
var origin=event.origin;
}
JSONP
JSONP利用<script>標籤的跨域能力實現跨域數據的訪問,請求動態生成的JavaScript腳本同時帶一個callback函數名作為參數。其中callback函數本地文檔的JavaScript函數,服務器端動態生成的腳本會產生數據,並在代碼中以產生的數據為參數調用callback函數。當這段腳本加載到本地文檔時,callback函數就被調用。
為了動態實現JSONP請求,可以使用Javascript動態插入<script>標籤:
<script type="text/javascript">
// this shows dynamic script insertion
var script = document.createElement('script');
script.setAttribute('src', url);
// load the script
document.getElementsByTagName('head')[0].appendChild(script);
</script>
WebSocket
現代瀏覽器允許腳本直連一個WebSocket地址而不管同源策略。然而,使用WebSocket URI的時候,在請求中插入Origin頭就可以標識腳本請求的源。為了確保跨站安全,WebSocket服務器必須根據允許接受請求的白名單中的源列表比較頭數據。與JSONP方法不同的是,該響應函數被傳入到創建<script> 標籤的構造函數中,檢測到已經成功接受到收據的狀態後再執行函數