根據(jù)同源策略,瀏覽器默認是不允許XMLHttpRequest對象問非同一站點下的資源的,即用ajax方式訪問非同一域名下的資源會出錯。比如當google要通過ajax去訪問百度的數(shù)據(jù),是不行的。
創(chuàng)新互聯(lián)是一家專業(yè)提供陵城企業(yè)網(wǎng)站建設(shè),專注與成都網(wǎng)站建設(shè)、做網(wǎng)站、html5、小程序制作等業(yè)務(wù)。10年已為陵城眾多企業(yè)、政府機構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站建設(shè)公司優(yōu)惠進行中。
所謂同源,是要求協(xié)議,域名,端口都相同。
比如 http://www.aaa.com 和下列URL相比,都不屬于同源。
https://www.aaa.com
http://www.aaa.com:8080
http://aaa.com
但下面這種屬于同源:
http://username:password@www.aaa.com
禁用跨域訪問資源是為了安全,但會犧牲便利性。因此就出現(xiàn)了好幾種跨域訪問資源的方法。其中一種是稱之為CORS的技術(shù)(Cross-origin resource sharing)。
CORS的概念
所謂CORS(Cross-origin resource sharing ),是指通過XMLHttpRequest的ajax方式訪問其他域名下資源,而不是在A域名的頁面上點擊打開一個B域名的頁面。引入不同域上的js腳本文件也是沒用問題的。出于安全考慮,跨域請求不能訪問document.cookie對象。
CORS技術(shù)允許跨域訪問多種資源,比如javascript,字體文件等,這種技術(shù)對XMLHttpRequest做了升級,使之可以進行跨域訪問。但不是所有的瀏覽器都支持CORS技術(shù)。Firefox和Chrome等瀏覽器支持的比較好,稍微新一點的版本都支持。IE比較搓,IE10才真正支持這個機制,IE10以下需要用XDomainRequest這個對象,這是IE特有的。
當然不是說瀏覽器支持了就立刻可以跨域訪問了,CORS技術(shù)中最重要的關(guān)鍵點是響應(yīng)頭里的Access-Control-Allow-Origin這個Header。 此Header是W3C標準定義的用來檢查是可否接受跨域請求的一個標識。實現(xiàn)過程大致如下:A域名中發(fā)起跨域請求到B域名,B域名如果發(fā)送響應(yīng)頭Access-Control-Allow-Origin: A域名,那么A域名的這次跨域請求就能成功。反之則不成功。這里有一個稱之為Preflighted requests 的步驟,這一步驟中,瀏覽器發(fā)起一個OPTIONS請求,去服務(wù)器驗證是否支持跨域訪問。(
用chrome去查看這個option請求始終看不到,只有當跨域訪問請求非正常(比如本人筆誤,將請求type的get誤寫成了gey)的情況下,才會看到有個OPTIONS請求,如圖:
非常奇怪,不知道為何。
CORS的實現(xiàn)
舉個例子。
有2個站點,分別綁定了www.myhost1.com和www.myhost2.com:8001這個兩個域名。
假設(shè)www.myhost2.com:8001下的a.html文件,需要訪問www.myhost1.com域名下的b.aspx文件,代碼如下。
a文件代碼如下,點擊按鈕就會去跨域訪問b頁面:
Apage
b文件為一個aspx文件,代碼非常簡單,顯示時間
<%@ Page Language="C#" %> <%=DateTime.Now%>
A文件的地址為http://www.myhost2.com:8001/a.html
A文件的請求是按照get方式讀取的,當A文件點擊按鈕,會彈出B文件的內(nèi)容。由于同源策略,會看到如下錯誤:
為了避免這個錯誤,在www.myhost1.com主機的Http響應(yīng)標頭里加上Access-Control-Allow-Origin:http://www.myhost2.com:8001,注意不要打上最后一個斜杠/,意思是允許http://www.myhost2.com:8001這個域名中的XMLHttpRequest對象跨域訪問www.myhost1.com主機下的資源。
還有一種方法是修改web.config,Access-Control-Allow-Origin的值可以是通配符*,比如如下:
或者通過代碼添加響應(yīng)標頭來實現(xiàn)。
<%@ Page Language="C#" %> <% Response.AppendHeader("Access-Control-Allow-Origin", "http://www.myhost2.com:8001"); %> <%=DateTime.Now%>
當瀏覽器通過ajax請求訪問其他域名下的資源時,如果那個資源的響應(yīng)頭中包含了Access-Control-Allow-Origin,則可以請求成功,否則就會失敗,是否設(shè)置了響應(yīng)頭,可以在firebug等調(diào)試工具內(nèi)看到。
無響應(yīng)頭
有響應(yīng)頭
跨域訪問的風(fēng)險
默認情況下,CORS機制不讀取cookie值,即跨域訪問時沒有帶上cookie,當需要跨域訪問帶cookie時,需要設(shè)置另一個文件頭,Access-Control-Allow-Credentials,值為true。該選項設(shè)定了是否允許跨域請求帶cookie。當設(shè)定為true后,對于XMLHttpRequest還需要設(shè)定withCredentials為true,在Jquery中為xhrFields: {withCredentials: true}
一旦允許了帶cookie的跨域訪問,那么遭到CSRF***的概率大大的增加了,比如之前的假設(shè)a.html頁面內(nèi)有一段惡意的js,它每隔1分鐘去跨域請求用戶b頁面,并將請求后的數(shù)據(jù)偷偷的存入自己的數(shù)據(jù)庫內(nèi)。假設(shè)b頁面需在要用戶登錄的情況下,可以請求到很多機密數(shù)據(jù),比如信用卡號等,那當用戶打開a頁面后,在非授意的情況下,獲取到b面值的內(nèi)容,引發(fā)泄密。
對之前的演示代碼做小的修改:
a頁面代碼:
Apage
b.aspx頁面代碼如下:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="b.aspx.cs" %> <% Response.AppendHeader("Access-Control-Allow-Origin", " http://www.myhost2.com:8001 "); Response.AppendHeader("Access-Control-Allow-Credentials", "true"); if (Session["islog"] != null) { if (Session["islog"] == "1") { Response.Write("已登錄,顯示機密信息 "); } } else { Response.Write("未登錄"); } if (Request["login"] == "true") { Session["islog"] = "1"; Session.Timeout = 1; Response.Write("Login in OK"); } %> <%=DateTime.Now%>
假設(shè)b頁面的用戶登錄了一下,種植了一個session,那么就會有一個sessionid被存儲到用戶的cookie中去,此時,a頁面點擊按鈕后,會獲取b頁面顯示的內(nèi)容,根據(jù)***的行為,可以有更大的破壞力。
演示圖:當b頁面未登錄時,顯示未登錄。請求中不帶cookie。
但是當b頁面已經(jīng)登錄,則在a頁面中的跨域請求中,帶有cookie,會顯示已登錄的頁面。
參考資料:
http://drops.wooyun.org/tips/188
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
https://dev.opera.com/articles/dom-access-control-using-cors/
http://blog.darkthread.net/post-2014-09-29-cors-options-preflight-and-iis.aspx
http://www.cnblogs.com/idche/p/3190926.html