XSS中的同源策略和跨域問題

轉自 https://www.cnblogs.com/chaoyuehedy/p/5556557.htmljavascript

 

1 同源策略

所謂同源策略,指的是瀏覽器對不一樣源的腳本或者文本的訪問方式進行的限制。好比源a的js不能讀取或設置引入的源b的元素屬性。html

那麼先定義下什麼是同源,所謂同源,就是指兩個頁面具備相同的協議,主機(也常說域名),端口,三個要素缺一不可。java

能夠看下面的幾個示例來更加清楚的瞭解一下同源的概念:node

URL1 URL2 說明 是否容許通訊
http://www.foo.com/js/a.js http://www.foo.com/js/b.js 協議、域名、端口都相同 容許
http://www.foo.com/js/a.js http://www.foo.com:8888/js/b.js 協議、域名相同,端口不一樣 不容許
https://www.foo.com/js/a.js http://www.foo.com/js/b.js 主機、域名相同,協議不一樣 不容許
http://www.foo.com/js/a.js http://www.bar.com/js/b.js 協議、端口相同,域名不一樣 不容許
http://www.foo.com/js/a.js http://foo.com/js/b.js 協議、端口相同,主域名相同,子域名不一樣 不容許

同源策略限制了不一樣源之間的交互,可是有人也許會有疑問,咱們之前在寫代碼的時候也經常會引用其餘域名的js文件,樣式文件,圖片文件什麼的,沒看到限制啊,這個定義是否是錯了。其實否則,同源策略限制的不一樣源之間的交互主要針對的是js中的XMLHttpRequest等請求,下面這些狀況是徹底不受同源策略限制的。python

  • 頁面中的連接,重定向以及表單提交是不會受到同源策略限制的。連接就不用說了,導航網站上的連接都是連接到其餘站點的。而你在域名www.foo.com下面提交一個表單到www.bar.com是徹底能夠的。
  • 跨域資源嵌入是容許的,固然,瀏覽器限制了Javascript不能讀寫加載的內容。如前面提到的嵌入的<script src="..."></script>,<img>,<link>,<iframe>等。固然,若是要阻止iframe嵌入咱們網站的資源(頁面或者js等),咱們能夠在web服務器加上一個X-Frame-Options DENY頭部來限制。nginx就能夠這樣設置add_header X-Frame-Options DENY;

既然有這麼多的狀況是沒有同源策略限制的,那麼一般的跨域問題從何而來呢?轉到下一節跨域問題。jquery

2 跨域問題

這一節來討論下跨域問題,固然前置條件是咱們在WEB服務器或者服務端腳本中設置ACCESS-CONTROL-ALLOW-ORIGIN頭部,若是設置了這些頭部並容許某些域名跨域訪問,則瀏覽器就會跳過同源策略的限制返回對應的內容。此外,若是你不是經過瀏覽器去獲取資源,好比你經過一個python腳本去調用接口或者獲取js文件,也不在這個限制以內。nginx

2.1 Ajax跨域

經過ajax調用其餘域的接口會有跨域問題。好比下面的例子,我在http://www.foo.com/index.html中經過ajax調用請求http://www.bar.com/js/test.js頁面,此時是會報錯的。web

XMLHttpRequest cannot load http://www.bar.com/js/test.js. 
No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'http://www.foo.com' is therefore not allowed access.

這是由於咱們的WEB服務器沒有設置ACCESS-CONTROL-ALLOW-ORIGIN頭部,默認狀況下是禁止跨域引用資源的。固然這裏有一點要注意,其實這個跨域請求是發送成功了的,服務器也有返回test.js內容,只是瀏覽器禁止Javascript去取response的數據而已。若是要設置ACCESS-CONTROL-ALLOW-ORIGIN頭部,nginx能夠使用下面的代碼ajax

add_header 'Access-Control-Allow-Origin' 'http://www.foo.com';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET,POST';

另外,咱們看到直接經過Javascript去取iframe中的元素也是會報錯的,由於域名不一樣。報錯以下所示json

Uncaught SecurityError: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "http://www.foo.com" from accessing a frame with origin "http://foo.com". 
The frame being accessed set "document.domain" to "foo.com", but the frame requesting access did not. Both must set "document.domain" to the same value to allow access.

 

這時由於咱們的主域名相同,所以能夠在index.html和test.html中設置document.domain='foo.com'來訪問iframe中的元素。注意,是兩個域名下面的文件都要設置,即使是一樣的主域名。固然這是特例,若是是兩個徹底不一樣的域名,是沒有辦法的,你不能把www.foo.comdomain設置成www.163.com

此外,在index.html裏面也能夠看到經過<script>,<iframe>等標籤都是能夠跨域內嵌資源的。

# index.html
<!DOCTYPE html>
<html>
<head>
<title>test cross domain</title>
<script src="/js/jquery.js"></script>
<script src="http://www.bar.com/js/test.js"></script>
<script>
$(function(){
    document.domain = 'foo.com'; //1 註釋掉則會報錯 
    var ifr = document.getElementById("testframe");
    ifr.onload = function(){
        var doc = ifr.contentDocument || ifr.contentWindow.document;
        alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
    }
});

$.ajax("http://www.bar.com/js/test.js"); //2 報錯
</script>

</head>
<body>
<h1>Test Cross Domain</h1>
<iframe id="testframe" src="http://foo.com/test.html"></iframe>
</body>
</html>

固然還能夠經過iframe,location.hash,window.name,HTML5的postMessage等方法來實現跨域資源訪問,更多內容參見Rain Man的這篇文章 JavaScript跨域總結和解決辦法

2.2 JSONP跨域訪問

JSONP也是開發中常見到的內容,在jquery中就有封裝,經過ajax請求多帶上一個jsonp的參數便可。JSONP也可以實現跨域訪問資源,可是它的實現原理其實跟ajax沒有多少關係,它是經過動態插入<script>標籤來實現跨域資源訪問的,由於根據前面內容咱們已經知道,嵌入跨域資源瀏覽器是容許的。

下面經過一個簡單的例子來講明JSONP的原理。
兩個文件,第一個是http://www.foo.com/jsonp.html,經過動態建立script標籤加載了http://www.bar.com/js/outer.js文件,而後outer.js文件返回的內容正好是一個函數調用,如此,實現了數據傳遞和回調過程。固然,實際的jsonp接口中,會讓你傳一個函數名過去,而後返回的數據中回調函數名就是你傳的函數名,回調函數的參數則是封裝的json格式。jQuery中的jsonp實現原理基本就是這樣,更詳細的jsonp原理能夠參見這篇大做深刻淺出JSONP

# jsonp.html
<script type="text/javascript">
    function callback(data) {
        alert(data.message);
    }
    function addScriptTag(src){
    var script = document.createElement('script');
        script.src = src;
        document.body.appendChild(script);
    }

    window.onload = function(){
        addScriptTag("http://www.foo.com/js/outer.js");
    }
</script>

# outer.js
callback({message:"success"});
相關文章
相關標籤/搜索