同源策略為什麼而生?
JS可以讀取/修改網頁的值。
一個瀏覽器中,開啟一個銀行網站和一個惡意網站,如果惡意網站能夠對銀行網站進行修改,那麼就會很危險。
你開啟了惡意網站和另一個網站,如果沒有同源限制,該惡意網站就可以構造AJAX請求頻繁在另一個網站發廣告帖。
同源策略就是為了解決這類問題而出現的。
同源策略限制一個載入於A origin的document或者script能夠如何和來自於另外一個origin的resource互動。同源策略是隔離潛在惡意網頁的安全機制。
瀏覽器的同源策略,限制了來自不同源的"document"或指令碼,對當前"document"讀取或設定某些屬性。 在瀏覽器中,<script>、<img>、<iframe>、<link>等標籤都可以載入跨域資源,同源策略只對網頁的HTML文件做了限制,對載入的其他靜態資源如javascript、css、圖片等仍然認為屬於同源。
源的定義
兩個網頁只有具有相同的protocol,port以及host才被認為是具有相同的origin的。
比如http://xxx.yyy.com:8000/zzz/page.html和http://xxx.yyy.com:8000/kkk/index.html具有相同的origin
about:blank
, javascript:
and data:
URLs則從載入那個URL的檔案中繼承origin.
同源策略控制了異源之間的互操作,比如,當你使用XMLHttpRequest或者一個<img>元素時就存在這個問題。這些互操作(interactions)典型地放在三個category中:
- cross-origin writes通常是被允許的,比如links,redirect,或者提交一個表單form.
- cross-origin embedding也通常是被允許的,例如:
- 使用<script src="..."></script>引入的javascript,這種情況下關於語法錯誤的錯誤訊息只在同源的指令碼中存在;(Content-Type:text/javascript或者application/javascript)
- 使用<link rel="stylesheet" href="...">來引入的css。注意由於css的語法rule,跨域的css需要一個合適的content-type header (Content-Type:text/css)
- 使用<img>標籤引入的圖片,包括PNG,JPEG,GIF,BMP,SVG等。。。
- 使用<video><embed>或者<applet>來引入的plug-in
- 使用@font-face引入的字型.但是要注意雖然chrome能很好的工作,但是有些瀏覽器,比如firefox,ie可能不允許非同源的字型檔案被載入,這種情況下,如果使用你自己的CDN網路(這很普遍),則需要配置
# Apache config <FilesMatch ".(eot|ttf|otf|woff)"> Header set Access-Control-Allow-Origin "*" </FilesMatch>
-
- 任何使用<frame>和<iframe>引入的東西。注意:一個網站可以通過使用
X-Frame-Options
頭來防止你自己的網頁被別人frame過去!
- 任何使用<frame>和<iframe>引入的東西。注意:一個網站可以通過使用
- cross-origin read一般是不允許的,但是一般如果通過embed方式來呼叫則往往會洩漏部分讀的許可權。比如,你可以讀取到embedded image的寬和高
如何允許跨源訪問呢?使用CORS機制吧
var invocation = new XMLHttpRequest(); var url = 'http://bar.other/resources/public-data/'; function callOtherDomain() { if(invocation) { invocation.open('GET', url, true); invocation.onreadystatechange = handler; invocation.send(); } }
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
如何阻止跨域(源)訪問呢?
- 為了組織cross-origin writes,通過判斷一個在request中存在的不易猜出的token(CSRF)TOKEN。注意你必須阻止cross-origin reads of pages that know this token.
- 為了阻止cross-origin reads of a resource,必須確保它是不能被embedded(not embeddable)。通常,我們是需要阻止embedding的,因為embedding一個resource通常會洩漏它的部分資訊的!
- 為了阻止cross-origin embedding,確保你的資源不能被翻譯為上述embeddable格式中的任何一種。瀏覽器大多數情況下並不會respect Content-Type.比如如果你將<script>tag指向一個html文件,那麼瀏覽器則將盡自己所能將HTML解析為javascript.當你的資源不是一個你站點的入口時,你可以使用CSRF token來阻止embedding.
Cross-orgin script API access
Javascript API,比如iframe.contentWindow, window.parent, window.open,window.opener允許documents來直接引用彼此。當兩個document不同源時,這些reference則僅對Window和Location物件開放相當有限的訪問許可權,下面將分別列出。
https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
不遵循同源限制的標籤:
<script> <img> <iframe>中的src,href都可以任意連結網路資源,相當於對所要求的源進行了一次請求。
源繼承:
來自about:blank,JavaScript:和data:URLs中的內容,繼承了將其載入的文件所指定的源,因為它們的URL本身未指定任何關於自身源的資訊。
AJAX跨域的問題
Ajax (XMLHttpRequest)請求應該受到同源策略的限制,但是我們來看實際情況:
Ajax通過XMLHttpRequest能夠與遠端的伺服器進行資訊互動,另外XMLHttpRequest是一個純粹的Javascript物件,這樣的互動過程,是在後臺進行的,使用者不易察覺。
因此,XMLHTTP實際上已經突破了原有的Javascript的安全限制。
舉個例子:
假設某網站引用了其它站點的javascript,這個站點被compromise並在javascript中加入獲取使用者輸入並通過ajax提交給其他站點,這樣就可以源源不斷收集資訊。
或者某網站因為存在漏洞導致XSS注入了javascript指令碼,這個指令碼就可以通過ajax獲取使用者資訊並通過ajax提交給其他站點,這樣就可以源源不斷收集資訊。
如果我們又想利用XMLHTTP的無重新整理非同步互動能力,又不願意公然突破Javascript的安全策略,可以選擇的方案就是給XMLHTTP加上嚴格的同源限制。
這樣的安全策略,很類似於Applet的安全策略。IFrame的限制還僅僅是不能訪問跨域HTMLDOM中的資料,而XMLHTTP則根本上限制了跨域請求的提交。(實際上下面提到了CORS已經放寬了限制)
隨著Ajax技術和網路服務的發展,對跨域的要求也越來越強烈。下面介紹Ajax的跨域技術。
2.1 JSONP
請參考: http://www.jb51.net/article/75484.htm
JSONP技術實際和Ajax沒有關係。我們知道<script>標籤可以載入跨域的javascript指令碼,並且被載入的指令碼和當前文件屬於同一個域。因此在文件中可以呼叫/訪問指令碼中的資料和函式。如果javascript指令碼中的資料是動態生成的,那麼只要在文件中動態建立<script>標籤就可以實現和服務端的資料互動。
JSONP就是利用<script>標籤的跨域能力實現跨域資料的訪問,請求動態生成的JavaScript指令碼同時帶一個callback函式名作為引數。其中callback函式本地文件的JavaScript函式,伺服器端動態生成的指令碼會產生資料,並在程式碼中以產生的資料為引數呼叫callback函式。當這段指令碼載入到本地文件時,callback函式就被呼叫。