同源策略限制了一個源(origin)中加載文本或腳本與來自其它源(origin)中資源的交互方式。javascript
若是兩個頁面擁有相同的協議(protocol),端口(若是指定),和主機, 那麼這兩個頁面就屬於同一個源(origin)。
下表給出了相對http://store.company.com/dir/page.html同源檢測的示例:html
頁面能夠改變自己的源,但會受到一些限制。腳本能夠設置document.domain 的值爲當前域的一個後綴,若是這樣作的話,短的域將做爲後續同源檢測的依據。java
例如,假設在 http://store.company.com/dir/other.html中的一個腳本執行了下列語句:web
document.domain = "company.com";
這條語句執行以後,頁面將會成功地經過對 http://company.com/dir/page.html 的同源檢測。ajax
瀏覽器單獨保存端口號。任何的賦值操做,包括document.domain = document.domain都會以null值覆蓋掉原來的端口號。 所以company.com:8080頁面的腳本不能僅經過設置document.domain = "company.com"就能與company.com通訊。賦值時必須帶上端口號,以確保端口號不會爲null。shell
附註:使用document.domain來讓子域安全地訪問其父域,須要同時將子域和父域的document.domain設置爲相同的值。必需要這麼作,即便是簡單的將父域設置爲其原來的值。沒有這麼作的話可能致使受權錯誤。後端
可是實踐中有一些場景須要跨域的讀寫,因此出現了一些hack的方式來跨域。好比在同域內作一個代理,JSON-P等。 但這些方式都存在缺陷,沒法完美的實現跨域讀寫。因此在XMLHttpRequest v2標準下,提出了CORS(Cross Origin Resourse-Sharing)的模型,試圖提供安全方便的跨域讀寫資源。目前主流瀏覽器均支持CORS。跨域
CORS定義了兩種跨域請求,簡單跨域請求和非簡單跨域請求。瀏覽器
當一個跨域請求發送簡單跨域請求包括緩存
提及來比較彆扭,簡單的意思就是設置了一個白名單,符合這個條件的纔是簡單請求。其餘不符合的都是非簡單請求。以下圖所示:
之因此有這個分類是由於瀏覽器對簡單請求和非簡單請求的處理機制是不同的。當咱們須要發送一個跨域請求的時候,瀏覽器會首先檢查這個請求,若是它符合上面所述的簡單跨域請求,瀏覽器就會馬上發送這個請求。
若是瀏覽器檢查以後發現這是一個非簡單請求,好比請求頭含有X-Forwarded-For字段。這時候瀏覽器不會立刻發送這個請求,而是有一個preflight,跟服務器驗證的過程。瀏覽器先發送一個options方法的預檢請求。下圖是一個示例。若是預檢經過,則發送這個請求,不然就不拒絕發送這個跨域請求。
下面詳細分析一下實現安全跨域請求的控制方式。先看一下非簡單請求的預檢過程。
Origin: 普通的HTTP請求也會帶有,在CORS中專門做爲Origin信息供後端比對,代表來源域。 Access-Control-Request-Method: 接下來請求的方法,例如PUT, DELETE等等 Access-Control-Request-Headers: 自定義的頭部,全部用setRequestHeader方法設置的頭部都將會以逗號隔開的形式包含在這個頭中
Access-Control-Allow-Origin: Access-Control-Allow-Methods: Access-Control-Allow-Headers:
Access-Control-Allow-Origin: 容許跨域訪問的域,能夠是一個域的列表,也能夠是通配符"*"。這裏要注意Origin規則只對域名有效,並不會對子目錄有效。即http://foo.example/subdir/ 是無效的。可是不一樣子域名須要分開設置,這裏的規則能夠參照同源策略 Access-Control-Allow-Credentials: 是否容許請求帶有驗證信息,這部分將會在下面詳細解釋 Access-Control-Expose-Headers: 容許腳本訪問的返回頭,請求成功後,腳本能夠在XMLHttpRequest中訪問這些頭的信息(貌似webkit沒有實現這個) Access-Control-Max-Age: 緩存這次請求的秒數。在這個時間範圍內,全部同類型的請求都將再也不發送預檢請求而是直接使用這次返回的頭做爲判斷依據,很是有用,大幅優化請求次數 Access-Control-Allow-Methods: 容許使用的請求方法,以逗號隔開 Access-Control-Allow-Headers: 容許自定義的頭部,以逗號隔開,大小寫不敏感
而後瀏覽器經過返回結果的這些控制字段來決定是將結果開放給客戶端腳本讀取仍是屏蔽掉。若是服務器沒有配置cors,返回結果沒有控制字段,瀏覽器會屏蔽腳本對返回信息的讀取。
你們注意這個流程。服務器接收到跨域請求的時候,並無先驗證,而是先處理了請求。因此從某種程度上來講。在支持cors的瀏覽器上實現跨域的寫資源,打破了傳統同源策略下不能跨域讀寫資源。
再一個就是若是程序猿偷懶將Access-Control-Allow-Origin設置爲容許來自全部域的跨域請求。那麼cors的安全機制幾乎就無效了。不過先別高興的太早。其實這裏在設計的時候有一個很好的限制。xmlhttprequest發送的請求須要使用「withCredentials」來帶上cookie,若是一個目標域設置成了容許任意域的跨域請求,這個請求又帶着cookie的話,這個請求是不合法的。(就是若是須要實現帶cookie的跨域請求,須要明確的配置容許來源的域,使用任意域的配置是不合法的)瀏覽器會屏蔽掉返回的結果。javascript就無法獲取返回的數據了。這是cors模型最後一道防線。假如沒有這個限制的話,那麼javascript就能夠獲取返回數據中的csrf token,以及各類敏感數據。這個限制極大的下降了cors的風險。
從思路上講,有兩種類型的攻擊方式。
Access-Control-Allow-Origin: * 容許任何來自任意域的跨域請求
用戶訪問惡意網頁的時候,執行了到內網服務器192.168.1.123/password.txt的請求,腳本在接收到服務器返回以後,將內容發送到攻擊者的服務器上。
目前來講,解決思路有兩種
Access-Control-Allow-Origin: allow.domain.com