CORS跨域資源共享漏洞

有關於瀏覽器的同源策略和如何跨域獲取資源,傳送門——> 瀏覽器同源策略和跨域的實現方法

跨域資源共享(CORS)是一種放寬同源策略的機制,以使不同的網站可以跨域獲取數據。CORS非常有用,可以共享許多內容,不過這裏存在風險。因爲它完全是一個盲目的協議,只是通過HTTP頭來控制的。那麼,CORS跨域資源共享漏洞是怎麼發生的呢?由於程序員配置不當,Origin源不嚴格,從而造成跨域問題。

我們先來簡單分析一下CORS的跨域獲取資源過程:

CORS定義了兩種跨域請求:簡單跨域請求非簡單跨域請求。簡單跨域請求就是使用設定的請求方式請求數據,而非簡單跨域請求則是在使用設定的請求方式請求數據之前,先發送一個OPTIONS預檢請求,看服務端是否允許客戶端發送非簡單請求。只有"預檢"通過後纔會再發送一次請求用於數據傳輸。

當我們需要發送一個跨域請求的時候,瀏覽器會首先檢查這個請求,如果它是簡單跨域請求,瀏覽器就會立刻發送這個請求。如果它是非簡單跨域請求,這時候瀏覽器不會馬上發送這個請求,而是有一個跟服務器預檢驗證的過程。

簡單跨域請求

當一個跨域請求發送簡單跨域請求時包括:

  • 請求方法爲HEAD、GET、POST;
  • 請求頭只有4個字段,Accept、Accept-Language、Content-Language、Last-Event-ID。如果請求頭還設置了Content-Type,則其值只能是 application/x-www-form-urlencoded,multipart/form-data,text/plain。

只有同時滿足以上兩個條件時,纔是簡單請求,否則爲非簡單請求。

在簡單請求中,瀏覽器進行跨域請求,會在請求中攜帶Origin,表面這是一個跨域。服務端會在接收中,通過自己的跨域規則進行驗證。通過Access-Control-Allow-Origin和Access-Control-Allow-Methods ,如果驗證成功,則會返回資源內容,如果驗證失敗,則返回403狀態。

非簡單跨域請求

下面簡單分析一下非簡單跨域請求的過程。瀏覽器先發送一個OPTIONS方法的預檢請求。帶有如下字段:

  • Origin: 普通的HTTP請求也會帶有,在CORS中專門作爲Origin信息供後端比對,表明來源域。
  • Access-Control-Request-Method:  接下來請求的方法,例如PUT、DELETE等等
  • Access-Control-Request-Headers: 自定義的頭部,所有用setRequestHeader方法設置的頭部都將會以逗號隔開的形式包含在這個頭中

然後如果服務器配置了CORS,會返回對應對的字段,具體字段含義在返回結果是一併解釋。

  • 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中訪問這些頭的信息
  • Access-Control-Max-Age: 緩存此次請求的秒數。在這個時間範圍內,所有同類型的請求都將不再發送預檢請求而是直接使用此次返回的頭作爲判斷依據,非常有用,大幅優化請求次數
  • Access-Control-Allow-Methods: 允許使用的請求方法,以逗號隔開
  • Access-Control-Allow-Headers: 允許自定義的頭部,以逗號隔開,大小寫不敏感

然後瀏覽器通過返回結果的這些控制字段來決定是將結果開放給客戶端腳本讀取還是屏蔽掉。如果服務器沒有配置CORS,返回結果沒有控制字段,瀏覽器會屏蔽腳本對返回信息的讀取。

所以,網站可以通過發送以下HTTP響應頭部來啓用CORS:

Access-Control-Allow-Origin: https://example.com

這樣的話,就可以允許指定的源(http://example.com)來跨域請求服務器端的資源,並且服務器會響應。在默認情況下,發送跨域請求時不會攜帶cookie或其他憑據。因此,它不能用於竊取與用戶相關的敏感信息(如CSRF令牌)。不過,網站服務器可以使用以下頭部來啓用憑據傳輸:

Access-Control-Allow-Credentials:true

這樣瀏覽器在請求數據的時候就需要帶上cookie,服務器在迴應的時候也會返回Cookie等一些信息進行雙向認證。

實現對單個域的信任是非常容易的事情。不過,如果需要信任多個域的話,那該怎麼辦呢?根據相關規範的建議,只需列出相關的域,並用空格加以分隔即可,例如:

Access-Control-Allow-Origin:http://a.example.com  http://example.com

但是,沒有哪個瀏覽器真正支持這一特性。

於是,我們可以通過使用通配符來信任所有子域,具體方法是:

Access-Control-Allow-Origin:  *.example.com

可是有一些偷懶的程序員,將Access-Control-Allow-Origin設置爲允許來自所有域*的跨域請求。

Access-Control-Allow-Origin:*

這樣,所有的網站都可以對其進行跨域資源請求了,這是非常危險的。不過先別高興的太早。其實這裏在設計的時候有一個很好的限制。xmlhttprequest發送的請求需要使用“withCredentials”來帶上cookie,如果一個目標域設置成了允許任意域的跨域請求,這個請求又帶着cookie的話,這個請求是不合法的。(就是如果需要實現帶cookie的跨域請求,需要明確的配置允許來源的域,使用任意域的配置是不合法的)瀏覽器會屏蔽掉返回的結果。javascript就沒法獲取返回的數據了。這是CORS模型最後一道防線。假如沒有這個限制的話,那麼javascript就可以獲取返回數據中的Cookie和CSRF Token,以及各種敏感數據。這個限制極大的降低了cors的風險。

如下,這是不允許的:

  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Credentials: true

這時,將在瀏覽器控制檯中收到以下錯誤消息:當憑證標誌爲true時,無法在Access-Control-Allow-Origin中使用通配符。

未完待續。。。


 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章