根據同源策略,瀏覽器默認是不容許XMLHttpRequest對象問非同一站點下的資源的,即用ajax方式訪問非同一域名下的資源會出錯。好比當google要經過ajax去訪問百度的數據,是不行的。javascript
所謂同源,是要求協議,域名,端口都相同。html
好比 http://www.aaa.com 和下列URL相比,都不屬於同源。java
https://www.aaa.comjquery
http://aaa.comajax
但下面這種屬於同源:chrome
http://username:password@www.aaa.com數據庫
禁用跨域訪問資源是爲了安全,但會犧牲便利性。所以就出現了好幾種跨域訪問資源的方法。其中一種是稱之爲CORS的技術(Cross-origin resource sharing)。c#
CORS的概念跨域
所謂CORS(Cross-origin resource sharing ),是指經過XMLHttpRequest的ajax方式訪問其餘域名下資源,而不是在A域名的頁面上點擊打開一個B域名的頁面。引入不一樣域上的js腳本文件也是沒用問題的。出於安全考慮,跨域請求不能訪問document.cookie對象。
CORS技術容許跨域訪問多種資源,好比javascript,字體文件等,這種技術對XMLHttpRequest作了升級,使之能夠進行跨域訪問。但不是全部的瀏覽器都支持CORS技術。Firefox和Chrome等瀏覽器支持的比較好,稍微新一點的版本都支持。IE比較搓,IE10才真正支持這個機制,IE10如下須要用XDomainRequest這個對象,這是IE特有的。
固然不是說瀏覽器支持了就馬上能夠跨域訪問了,CORS技術中最重要的關鍵點是響應頭裏的Access-Control-Allow-Origin這個Header。 此Header是W3C標準定義的用來檢查是能否接受跨域請求的一個標識。實現過程大體以下:A域名中發起跨域請求到B域名,B域名若是發送響應頭Access-Control-Allow-Origin: A域名,那麼A域名的此次跨域請求就能成功。反之則不成功。這裏有一個稱之爲Preflighted requests 的步驟,這一步驟中,瀏覽器發起一個OPTIONS請求,去服務器驗證是否支持跨域訪問。(
用chrome去查看這個option請求始終看不到,只有當跨域訪問請求非正常(好比本人筆誤,將請求type的get誤寫成了gey)的狀況下,纔會看到有個OPTIONS請求,如圖:
很是奇怪,不知道爲什麼。
CORS的實現
舉個例子。
有2個站點,分別綁定了www.myhost1.com和www.myhost2.com:8001這個兩個域名。
假設www.myhost2.com:8001下的a.html文件,須要訪問www.myhost1.com域名下的b.aspx文件,代碼以下。
a文件代碼以下,點擊按鈕就會去跨域訪問b頁面:
<!DOCTYPE html> <html lang="en" xmlns=" http://www.w3.org/1999/xhtml "> <head> <meta charset="utf-8" /> <title>Apage</title> <script type="text/javascript" src=" http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js "></script> <script> $(function () { var options = { type: 'get', url: " http://www.myhost1.com/mysite/cors/b.aspx ", success: function (result) { alert(result); } }; $("#btn1").click(function () { $.ajax(options); }); }); </script> </head> <body> <input type="button" id="btn1" value="load page b" /> </body> </html>
b文件爲一個aspx文件,代碼很是簡單,顯示時間
<%@ Page Language="C#" %> <%=DateTime.Now%>
A文件的地址爲http://www.myhost2.com:8001/a.html
A文件的請求是按照get方式讀取的,當A文件點擊按鈕,會彈出B文件的內容。因爲同源策略,會看到以下錯誤:
爲了不這個錯誤,在www.myhost1.com主機的Http響應標頭裏加上Access-Control-Allow-Origin:http://www.myhost2.com:8001,注意不要打上最後一個斜槓/,意思是容許http://www.myhost2.com:8001這個域名中的XMLHttpRequest對象跨域訪問www.myhost1.com主機下的資源。
還有一種方法是修改web.config,Access-Control-Allow-Origin的值能夠是通配符*,好比以下:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="*" /> </customHeaders> </httpProtocol> </system.webServer> </configuration>
或者經過代碼添加響應標頭來實現。
<%@ Page Language="C#" %> <% Response.AppendHeader("Access-Control-Allow-Origin", "http://www.myhost2.com:8001"); %> <%=DateTime.Now%>
當瀏覽器經過ajax請求訪問其餘域名下的資源時,若是那個資源的響應頭中包含了Access-Control-Allow-Origin,則能夠請求成功,不然就會失敗,是否設置了響應頭,能夠在firebug等調試工具內看到。
無響應頭
有響應頭
跨域訪問的風險
默認狀況下,CORS機制不讀取cookie值,即跨域訪問時沒有帶上cookie,當須要跨域訪問帶cookie時,須要設置另外一個文件頭,Access-Control-Allow-Credentials,值爲true。該選項設定了是否容許跨域請求帶cookie。當設定爲true後,對於XMLHttpRequest還須要設定withCredentials爲true,在Jquery中爲xhrFields: {withCredentials: true}
一旦容許了帶cookie的跨域訪問,那麼遭到CSRF***的機率大大的增長了,好比以前的假設a.html頁面內有一段惡意的js,它每隔1分鐘去跨域請求用戶b頁面,並將請求後的數據偷偷的存入本身的數據庫內。假設b頁面需在要用戶登陸的狀況下,能夠請求到不少機密數據,好比信用卡號等,那當用戶打開a頁面後,在非授意的狀況下,獲取到b面值的內容,引起泄密。
對以前的演示代碼作小的修改:
a頁面代碼:
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title>Apage</title> <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js"></script> <script> $(function () { var options = { type: 'get', xhrFields: {withCredentials: true}, url: "http://www.myhost1.com/mysite/cors/b.aspx", success: function (result) { alert(result); } }; $("#btn1").click(function () { $.ajax(options); }); }); </script> </head> <body> <input type="button" id="btn1" value="load page b" /> </body> </html>
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%>
假設b頁面的用戶登陸了一下,種植了一個session,那麼就會有一個sessionid被存儲到用戶的cookie中去,此時,a頁面點擊按鈕後,會獲取b頁面顯示的內容,根據***的行爲,能夠有更大的破壞力。
演示圖:當b頁面未登陸時,顯示未登陸。請求中不帶cookie。
可是當b頁面已經登陸,則在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