摘要:同源策略及跨域訪問(wèn)同源策略同源策略約束了兩個(gè)域之間資源的加載方式,是一個(gè)很重要的安全機(jī)制用來(lái)隔離那些有潛在安全隱患的文檔。
同源策略及跨域訪問(wèn) 同源策略
同源策略(Same-origin policy)約束了兩個(gè)域之間資源的加載方式,是一個(gè)很重要的安全機(jī)制用來(lái)隔離那些有潛在安全隱患的文檔。
何為源(orgin)一個(gè)源由一個(gè)URL的協(xié)議(protocol)、主機(jī)(host)和端口(port)進(jìn)行定義。如果兩個(gè)頁(yè)面擁有相同的協(xié)議、主機(jī)和端口一致的話,我們就可以稱它們?yōu)橥?。下面的表格比較了不同的URL跟http://store.company.com/dir/page.html這個(gè)URL的同源情況:
URL | 是否同源 | 理由 |
---|---|---|
http://store.company.com/dir2... | 是 | |
http://store.company.com/dir/... | 是 | |
https://store.company.com/sec... | 否 | 協(xié)議不同 |
http://store.company.com:81/d... | 否 | 端口不同 |
http://news.company.com/dir/o... | 否 | 主機(jī)不同 |
同源策略控制了兩個(gè)源之間的交互,例如你使用XMLHttpRequest發(fā)起一個(gè)請(qǐng)求,或者使用元素加載一張圖片,就會(huì)產(chǎn)生兩個(gè)源之間的交互。而當(dāng)兩個(gè)源不相同時(shí),有些交互會(huì)被允許,而有些則不被允許。而不允許的情況,就是我們常說(shuō)的跨域訪問(wèn)問(wèn)題。那什么情況下不同源的交互會(huì)被允許,什么情況下又不被允許呢?大致可以分為如下的情況:
鏈接、跳轉(zhuǎn)和表單提交這些在跨域的情況下都是被允許的。例如調(diào)用支付寶接口進(jìn)行支付就是典型的跨域表單提交的場(chǎng)景,這種跨域的調(diào)用是被允許的。但是這個(gè)很容易被利用來(lái)進(jìn)行CSRF攻擊,所以我們的表單提交需要做好這方面的防護(hù)。
跨域的資源內(nèi)嵌是被允許的。下面是一些資源內(nèi)容的例子:
使用加載Javascript。只有同源的腳本在語(yǔ)法錯(cuò)誤時(shí)會(huì)顯示錯(cuò)誤信息。
使用加載CSS。跨源的CSS文件要求使用正確的Content-Type 響應(yīng)頭。
使用加載圖片。
使用和加載媒體文件。
使用 、和加載插件。
使用 @font-face加載字體。有些瀏覽器允許加載跨域的字體,有些則不允許。
使用 和加載任何東西。
跨域文檔間使用Javascript腳本進(jìn)行交互,API的訪問(wèn)有限制。例如使用ifame嵌入的頁(yè)面或者使用window.open打開的頁(yè)面,如果跟父頁(yè)面不同源,則想通過(guò)Javascript去操作父頁(yè)面的DOM,是不允許的(反過(guò)來(lái)亦然)。
舉個(gè)例子,假設(shè)有這么一個(gè)頁(yè)面http://www.example.com/index.html:
.....
然后在http://sub.example.com/iframe.html頁(yè)面對(duì)父頁(yè)面的背景色進(jìn)行修改:
......
由于兩個(gè)頁(yè)面不同源,所以子頁(yè)面對(duì)父頁(yè)面的操作被禁止,例如在Firefox上你會(huì)看到以下的報(bào)錯(cuò):
Error: Permission denied to access property "document"
不同源之間的XMLHttpRequest調(diào)用(也就是我們常說(shuō)ajax調(diào)用)是不被允許的,這個(gè)也是我們最常遇到的跨域訪問(wèn)場(chǎng)景。例如我們?cè)?b>http://example.com/index.html頁(yè)面進(jìn)行如下的ajax調(diào)用:
var xhr = new XMLHttpRequest(); var url = "http://otherexample.com/api/get-data"; xhr.open("GET", url, true); xhr.onreadystatechange = handler; xhr.send();
在Firefox下會(huì)報(bào)如下錯(cuò)誤:
已攔截跨源請(qǐng)求:同源策略禁止讀取位于 http://otherexample.com/api/get-data 的遠(yuǎn)程資源。(原因:CORS 頭缺少 "Access-Control-Allow-Origin")。跨域訪問(wèn)的解決方案
一個(gè)頁(yè)面的源是可以修改的,修改的方法很簡(jiǎn)單,就是通過(guò)Javascript腳本設(shè)置document.domain的值。舉個(gè)例子,我們?cè)陧?yè)面http://store.company.com/dir/other.html執(zhí)行下面的代碼:
document.domain = "company.com";
那么這個(gè)頁(yè)面的域?qū)?huì)由store.company.com變成company.com,后面在判斷是否同源的時(shí)候,主機(jī)將會(huì)使用company.com這個(gè)值,而不是store.company.com。
那是不是修改域后就能跟同域的頁(yè)面進(jìn)行交互呢。答案是否定的。例如,如果你在頁(yè)面中通過(guò)iframe嵌入http://company.com/dir/page.html這個(gè)頁(yè)面,然后通過(guò)javascript去跟這個(gè)頁(yè)面交互,你會(huì)發(fā)現(xiàn)瀏覽器會(huì)報(bào)錯(cuò),就跟我們之前那個(gè)例子一樣。按照上面的源的定義,這時(shí)候兩個(gè)頁(yè)面應(yīng)該是同源的,為什么呢?因?yàn)樾薷?b>document.domain會(huì)導(dǎo)致端口號(hào)被設(shè)為null。所以另外一個(gè)頁(yè)面也需要把document.domain修改為相同的值,這樣兩個(gè)頁(yè)面的主機(jī)和端口就一致了,可以進(jìn)行互相的訪問(wèn)了。
既然源可以修改,那么是不是就解決了我們的跨域問(wèn)題呢?明顯沒(méi)這么簡(jiǎn)單。首先,這種方法是有很大限制條件的,document.domain這個(gè)值只能修改為這個(gè)頁(yè)面的當(dāng)前域或者當(dāng)前域的超級(jí)域。所以,這個(gè)方法只能解決同一超級(jí)域下的頁(yè)面跨域問(wèn)題。其次,它的使用場(chǎng)景也很有限,因?yàn)樗枰?yè)面執(zhí)行Javascript腳本,所以也就是說(shuō)一般只能應(yīng)用于頁(yè)面跟頁(yè)面的交互,例如訪問(wèn)ifame頁(yè)面或者window.open打開的頁(yè)面等等。所以如果你想用來(lái)解決ajax之類的跨域調(diào)用,這個(gè)方法就無(wú)能為力了。
使用代理也是解決跨域訪問(wèn)的一個(gè)方法。上面修改document.domain的方法只能用來(lái)訪問(wèn)子域名的頁(yè)面,無(wú)法訪問(wèn)不同域的頁(yè)面,而使用代理則沒(méi)有這個(gè)問(wèn)題。
例如我們有一個(gè)頁(yè)面http://example.com/,需要訪問(wèn)http://otherexample.com/這個(gè)頁(yè)面,我們不直接對(duì)這個(gè)頁(yè)面進(jìn)行訪問(wèn),而是通過(guò)請(qǐng)求另外一個(gè)同源的頁(yè)面,這個(gè)頁(yè)面在后端通過(guò)代理服務(wù)器把請(qǐng)求轉(zhuǎn)發(fā)到http://otherexample.com/,獲取數(shù)據(jù)并返回給客戶端。
另外,這個(gè)方法同樣可以用于解決ajax的跨域訪問(wèn)問(wèn)題。
JSONP也被經(jīng)常用來(lái)解決ajax的跨域調(diào)用問(wèn)題。JSONP請(qǐng)求并不是通過(guò)XMLHttpRequest發(fā)起,而是使用進(jìn)行調(diào)用。前面說(shuō)過(guò),內(nèi)嵌資源一般不受同源政策影響,所以可以加載其他源的資源。
舉個(gè)例子,假設(shè)http://www.example.com/頁(yè)面,想異步調(diào)用http://www.otherexample.com/ajax.json這個(gè)接口,這個(gè)接口會(huì)返回如下的數(shù)據(jù):
{ "id": "123", "name": "Captain Jack Sparrow" }
如果我們通過(guò)XMLHttpRequest發(fā)起調(diào)用,就會(huì)因?yàn)橥凑叨 K晕覀兺ㄟ^(guò)進(jìn)行調(diào)用,并通過(guò)參數(shù)傳遞我們的回調(diào)函數(shù)名:
然后接口獲取到callback函數(shù)名后,把原來(lái)返回的數(shù)據(jù)作為函數(shù)的參數(shù),最終返回如下的Javascript:
myFunction({"id": "123", "name": "Captain Jack Sparrow"});
然后myFunction就會(huì)執(zhí)行,達(dá)到了調(diào)用的目的。
這個(gè)方法在大多數(shù)情況下都很有用,但是它也有它的局限。一是它需要后端的配合,因?yàn)楹蠖说慕涌谛枰鶕?jù)約定的參數(shù)獲取回調(diào)函數(shù)名,然后跟返回?cái)?shù)據(jù)進(jìn)行拼接,最后進(jìn)行響應(yīng)。二是它只能進(jìn)行異步的調(diào)用,因?yàn)樗脑硎峭ㄟ^(guò)動(dòng)態(tài)生成加載JS的方法,而這個(gè)過(guò)程是異步的,所以如果你想進(jìn)行同步的調(diào)用,那么這個(gè)方法就無(wú)能為力了。
Web Messaging(又稱cross-document messaging)是HTML5的一個(gè)接口,允許兩個(gè)不同源的文檔之間進(jìn)行通信。
它主要用到了接口里的postMessage方法,這個(gè)方法可以把純文本消息從一個(gè)域發(fā)送到另外一個(gè)域。消息可以發(fā)送以下的對(duì)象:
發(fā)送方文檔里frame和iframe。
發(fā)送方通過(guò)Javascript打開的頁(yè)面。
發(fā)送方的父頁(yè)面。
打開發(fā)送方頁(yè)面的頁(yè)面。
消息event包含了以下的屬性:
data:收到的消息。
origin:發(fā)送方的源,包括協(xié)議、主機(jī)名和端口。
source:發(fā)送方的window對(duì)象。
舉個(gè)例子,假設(shè)example.net下的文檔A跟文檔里用iframe加載的example.com下的文檔B進(jìn)行通信,我們向文檔B發(fā)送消息Hello B,Javascript代碼大致如下:
var o = document.getElementsByTagName("iframe")[0]; o.contentWindow.postMessage("Hello B", "http://example.com/");
我們先獲取到文檔B的contentWindow對(duì)象,然后把需要發(fā)送到消息以及文檔B的源傳給postMessage。文檔B則通過(guò)監(jiān)聽message事件,捕獲到事件,并作相應(yīng)的處理:
function receiver(event) { if (event.origin == "http://example.net") { if (event.data == "Hello B") { event.source.postMessage("Hello A, how are you?", event.origin); } else { alert(event.data); } } } window.addEventListener("message", receiver, false);
需要注意的是,postMessage是個(gè)非阻塞的調(diào)用,也就是說(shuō)是異步的。
Web Messaging主要用于跨域文檔間的通訊,所以它不能用來(lái)解決所有跨域調(diào)用的問(wèn)題,例如ajax調(diào)用。而且IE瀏覽器對(duì)它的支持也很有限。
CORS(Cross-Origin Resource Sharing)是W3C提出的一個(gè)用于服務(wù)器端控制數(shù)據(jù)跨域傳輸?shù)囊粋€(gè)機(jī)制。 它的原理是通過(guò)一些新增加的HTTP頭讓服務(wù)端能定義哪些源的請(qǐng)求可以被允許。
簡(jiǎn)單舉個(gè)例子,假設(shè)我們?cè)陧?yè)面http://example.com/發(fā)起一個(gè)跨域的XMLHttpRequest請(qǐng)求:
var xhr = new XMLHttpRequest(); var url = "http://otherexample.com/api/get-data/"; xhr.open("GET", url, true); xhr.onreadystatechange = handler; xhr.send();
正常情況這個(gè)請(qǐng)求是不允許的。但是如果我們?cè)诜?wù)端返回以下響應(yīng)頭:
Access-Control-Allow-Origin: *
這個(gè)Access-Control-Allow-Origin頭表示服務(wù)端允許哪些源的請(qǐng)求,*表示允許所有的源,所以上面的請(qǐng)求就被允許了。當(dāng)然正常情況下我們不會(huì)這樣做,我們需要把Access-Control-Allow-Origin設(shè)置為真正想允許的源。在請(qǐng)求頭會(huì)有一個(gè)叫Origin的頭,它的值就是請(qǐng)求方的源(例如上面的請(qǐng)求會(huì)有Origin: http://example.com這個(gè)請(qǐng)求頭),服務(wù)端應(yīng)該根據(jù)這個(gè)頭去返回相應(yīng)的Access-Control-Allow-Origin頭。
當(dāng)然CORS的實(shí)際使用會(huì)比上面的例子復(fù)雜得多,具體可以參考MDN的這篇文章和W3C的規(guī)范。
CORS可以說(shuō)是解決XMLHttpRequest跨域調(diào)用的一個(gè)比較好的方法,但I(xiàn)E瀏覽器對(duì)它的支持同樣很有限,直到IE11才完全支持,所以在移動(dòng)端更能發(fā)揮它的作用。
參考https://developer.mozilla.org...
https://developer.mozilla.org...
https://www.sitepoint.com/wor...
https://en.wikipedia.org/wiki...
http://caniuse.com/#search=po...
http://caniuse.com/#search=cors
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/11219.html
摘要:三哪些會(huì)受到同源策略限制對(duì)于瀏覽器來(lái)說(shuō),除了會(huì)受到同源策略的限制外,瀏覽器加載的一些第三方插件也有各自的同源策略。九的現(xiàn)代瀏覽器允許腳本直連一個(gè)地址而不管同源策略。 一、Origin(源) 源由下面三個(gè)部分組成: 域名 端口 協(xié)議 兩個(gè) URL ,只有這三個(gè)都相同的情況下,才可以稱為同源。 下來(lái)就以 http://www.example.com/page.html 這個(gè)鏈接來(lái)比較說(shuō)...
摘要:扯了這么多,自然不是為了吹水,而是要為了引出前端開發(fā)的一個(gè)重要的知識(shí)點(diǎn)同源策略什么是同源策略出于保護(hù)用戶信息安全的目的,現(xiàn)在的瀏覽器都會(huì)實(shí)施同源策略這個(gè)政策,所謂同源策略指的是不同源的客戶端腳本在沒(méi)有明確授權(quán)情況下,不允許讀寫對(duì)方的資源。 導(dǎo)語(yǔ)你家的小孩帶了他的朋友來(lái)你們的家里玩,你家的小孩如果要在自家屋里拿玩具玩、拿東西吃你自然是不會(huì)阻止,但是如果你家小孩的朋友人品不行,亂拿東西吃、...
摘要:扯了這么多,自然不是為了吹水,而是要為了引出前端開發(fā)的一個(gè)重要的知識(shí)點(diǎn)同源策略什么是同源策略出于保護(hù)用戶信息安全的目的,現(xiàn)在的瀏覽器都會(huì)實(shí)施同源策略這個(gè)政策,所謂同源策略指的是不同源的客戶端腳本在沒(méi)有明確授權(quán)情況下,不允許讀寫對(duì)方的資源。 導(dǎo)語(yǔ)你家的小孩帶了他的朋友來(lái)你們的家里玩,你家的小孩如果要在自家屋里拿玩具玩、拿東西吃你自然是不會(huì)阻止,但是如果你家小孩的朋友人品不行,亂拿東西吃、...
摘要:同源策略是什么同源策略是瀏覽器的一個(gè)安全功能,不同源的數(shù)據(jù)禁止訪問(wèn)。或許你可以說(shuō)驗(yàn)證,在瀏覽器沒(méi)有同源策略的情況下這些都可以繞過(guò)去??偨Y(jié)同源策略是蠻好的,防御了大部分的攻擊。 前端最基礎(chǔ)的就是 HTML+CSS+Javascript。掌握了這三門技術(shù)就算入門,但也僅僅是入門,現(xiàn)在前端開發(fā)的定義已經(jīng)遠(yuǎn)遠(yuǎn)不止這些。前端小課堂(HTML/CSS/JS),本著提升技術(shù)水平,打牢基礎(chǔ)知識(shí)的中心思...
摘要:上節(jié)我們講了同源策略,這節(jié)我們講講如何跨域。當(dāng)這些從的腳本執(zhí)行出錯(cuò),因?yàn)檫`背了同源策略為了保證用戶信息不被泄露,錯(cuò)誤信息不會(huì)顯示出來(lái),取而代之只會(huì)返回一個(gè)。 前端最基礎(chǔ)的就是 HTML+CSS+Javascript。掌握了這三門技術(shù)就算入門,但也僅僅是入門,現(xiàn)在前端開發(fā)的定義已經(jīng)遠(yuǎn)遠(yuǎn)不止這些。前端小課堂(HTML/CSS/JS),本著提升技術(shù)水平,打牢基礎(chǔ)知識(shí)的中心思想,我們開課啦(每...
閱讀 1734·2021-11-12 10:36
閱讀 1641·2021-11-12 10:36
閱讀 3469·2021-11-02 14:46
閱讀 3857·2019-08-30 15:56
閱讀 3646·2019-08-30 15:55
閱讀 1495·2019-08-30 15:44
閱讀 1077·2019-08-30 14:00
閱讀 2758·2019-08-29 18:41