天天看點

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

在日常的項目開發時會不可避免的需要進行跨域操作,而在實際進行跨域請求時,經常會遇到類似 No 'Access-Control-Allow-Origin' header is present on the requested resource.這樣的報錯。

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

這樣的錯誤,一般是由于CORS跨域驗證機制設定不正确導緻的,本文将詳細講解CORS跨域驗證機制的原理,讓您輕松掌握CORS跨域設定的使用方法,安全、友善的進行前端開發。

什麼是CORS

CORS(Cross-Origin Resource Sharing 跨源資源共享),當一個請求url的協定、域名、端口三者之間任意一與目前頁面位址不同即為跨域。

例如最常見的,在一個域名下的網頁中,調用另一個域名中的資源。

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

相對于上面這種靜态的調用方式,還可以通過Ajax技術來動态發起跨域請求。例如如下的方式,利用XMLHttpRequest對象發送一個GET請求,擷取另一個域名下的圖檔内容。

CORS Test            
           

CORS的作用

為了改善網絡應用程式,開發人員要求浏覽器供應商允許跨域請求。跨域請求主要用于:

  • 調用XMLHttpRequest或fetchAPI通過跨站點方式通路資源
  • 網絡字型,例如Bootstrap(通過CSS使用@font-face 跨域調用字型)
  • 通過canvas标簽,繪制圖表和視訊。

CORS的安全隐患

跨域請求和Ajax技術都會極大地提高頁面的體驗,但同時也會帶來安全的隐患,其中最主要的隐患來自于CSRF(Cross-site request forgery)跨站請求僞造。

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

CSRF攻擊的大緻原理是:

  1. 使用者通過浏覽器,通路正常網站A(例如某銀行),通過使用者的身份認證(比如使用者名/密碼)成功A網站。
  2. 網站A産生Cookie資訊并傳回給使用者的浏覽器;
  3. 使用者保持A網站頁面登入狀态,在同一浏覽器中,打開一個新的TAB頁通路惡意網站B;
  4. 網站B接收到使用者請求後,傳回一些攻擊性代碼,請求A網站的資源(例如轉賬請求);
  5. 浏覽器執行惡意代碼,在使用者不知情的情況下攜帶Cookie資訊,向網站A送出請求。
  6. 網站A根據使用者的Cookie資訊核實使用者身份(此時使用者在A網站是已登入狀态),A網站會處理該請求,導緻來自網站B的惡意請求被執行。

CORS驗證機制

出于安全原因,浏覽器限制從腳本中發起的跨域HTTP請求。預設的安全限制為同源政策, 即JavaScript或Cookie隻能通路同域下的内容。W3C推薦了一種跨域的通路驗證的機制,即CORS(Cross-Origin Resource Sharing 跨源資源共享)。這種機制讓Web應用伺服器能支援跨站通路控制,使跨站資料傳輸更加安全,減輕跨域HTTP請求的風險。CORS驗證機制需要用戶端和服務端協同處理。

CORS浏覽器支援情況

目前主流浏覽器都已基本提供對跨域資源共享的支援,移動端浏覽器也幾乎全部支援。

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

用戶端處理機制

基于上述的CSRF的風險,各主流的浏覽器都會對動态的跨域請求進行特殊的驗證處理。驗證處理分為簡單請求驗證處理和預先請求驗證處理。

簡單請求

當請求同時滿足下面兩個條件時,浏覽器會直接發送GET請求,在同一個請求中做跨域權限的驗證。

請求方法是下列之一:

  • GET
  • HEAD
  • POST

請求頭中的Content-Type請求頭的值是下列之一:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

簡單請求時,浏覽器會直接發送跨域請求,并在請求頭中攜帶Origin 的header,表明這是一個跨域的請求。伺服器端接到請求後,會根據自己的跨域規則,通過Access-Control-Allow-Origin和Access-Control-Allow-Methods響應頭,來傳回驗證結果。如果驗證成功,則會直接傳回通路的資源内容。

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

如果驗證失敗,則傳回403的狀态碼,不會傳回跨域請求的資源内容。

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

可以通過浏覽器的Console檢視具體的驗證失敗原因

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

預先請求

當請求滿足下面任意一個條件時,浏覽器會先發送一個OPTION請求,用來與目标域名伺服器協商決定是否可以發送實際的跨域請求。

請求方法不是下列之一:

  • GET
  • HEAD
  • POST

請求頭中的Content-Type請求頭的值不是下列之一:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

浏覽器在發現頁面中有上述條件的動态跨域請求的時候,并不會立即執行對應的請求代碼,而是會先發送Preflighted requests(預先驗證請求),Preflighted requests是一個OPTION請求,用于詢問要被跨域通路的伺服器,是否允許目前域名下的頁面發送跨域的請求。

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

OPTIONS請求頭部中會包含以下頭部:Origin、Access-Control-Request-Method、Access-Control-Request-Headers。伺服器收到OPTIONS請求後,設定Access-Control-Allow-Origin、Access-Control-Allow-Method、Access-Control-Allow-Headers頭部與浏覽器溝通來判斷是否允許這個請求。如果Preflighted requests驗證通過,浏覽器才會發送真正的跨域請求。

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

如果Preflighted requests驗證失敗,則會傳回403狀态,浏覽器不會發送真正的跨域請求。

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

可以通過浏覽器的Console檢視具體的驗證失敗原因

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

帶認證的請求

預設情況下,跨源請求不提供憑據(cookie、HTTP認證及用戶端SSL證明等)。通過将withCredentials屬性設定為true,可以指定某個請求應該發送憑據。xhr.withCredentials = true;如果伺服器接收帶憑據的請求,會用下面的HTTP頭部來響應。Access-Control-Allow-Credentials: true伺服器還可以在Preflight響應中發送這個HTTP頭部,表示允許源發送帶憑據的請求。

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

如果發送的是帶憑據的請求,但伺服器的響應中沒有包含這個頭,那麼浏覽器就不會把響應交給JavaScript(responseText中将是空字元串,size為0)。

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

注意,當withCredentials屬性設定為true,需要response header中的'Access-Control-Allow-Origin'為一個确定的域名,而不能使用'*'這樣的通配符。

cors跨域_CORS——跨域請求那些事兒什麼是CORSCORS的作用CORS的安全隐患CORS驗證機制HTTP Header

服務端處理機制

伺服器端對于跨域請求的處理流程如下:

  1. 首先檢視http頭部有無origin字段;
  2. 如果沒有,或者不允許,直接當成普通請求處理,結束;
  3. 如果有并且是允許的,那麼再看是否是preflight(method=OPTIONS);
  4. 如果不是preflight(簡單請求),就傳回Allow-Origin、Allow-Credentials等,并傳回正常内容。
  5. 如果是preflight(預先請求),就傳回Allow-Headers、Allow-Methods等,内容為空;

HTTP Header

Request header

Origin

Origin頭在跨域請求或預先請求中,标明發起跨域請求的源域名。

Access-Control-Request-Method

Access-Control-Request-Method頭用于表明跨域請求使用的實際HTTP方法

Access-Control-Request-Headers

Access-Control-Request-Headers用于在預先請求時,告知伺服器要發起的跨域請求中會攜帶的請求頭資訊

Response header

Access-Control-Allow-Origin

Access-Control-Allow-Origin頭中攜帶了伺服器端驗證後的允許的跨域請求域名,可以是一個具體的域名或是一個*(表示任意域名)。簡單請求時,浏覽器會根據此響應頭的内容決定是否給腳本傳回相應内容,預先驗證請求時,浏覽器會根據此響應頭決定是否發送實際的跨域請求。

Access-Control-Expose-Headers

Access-Control-Expose-Headers頭用于允許傳回給跨域請求的響應頭清單,在清單中的響應頭的内容,才可以被浏覽器通路。

Access-Control-Max-Age

Access-Control-Max-Age用于告知浏覽器可以将預先檢查請求傳回結果緩存的時間,在緩存有效期内,浏覽器會使用緩存的預先檢查結果判斷是否發送跨域請求。

Access-Control-Allow-Credentials

Access-Control-Allow-Credentials用于告知浏覽器當withCredentials屬性設定為true時,是否可以顯示跨域請求傳回的内容。簡單請求時,浏覽器會根據此響應頭決定是否顯示響應的内容。預先驗證請求時,浏覽器會根據此響應頭決定在發送實際跨域請求時,是否攜帶認證資訊。

Access-Control-Allow-Methods

Access-Control-Allow-Methods用于告知浏覽器可以在實際發送跨域請求時,可以支援的請求方法,可以是一個具體的方法清單或是一個*(表示任意方法)。簡單請求時,浏覽器會根據此響應頭的内容決定是否給腳本傳回相應内容,預先驗證請求時,浏覽器會根據此響應頭決定是否發送實際的跨域請求。

Access-Control-Allow-Headers

Access-Control-Allow-Headers用于告知浏覽器可以在實際發送跨域請求時,可以支援的請求頭,可以是一個具體的請求頭清單或是一個*(表示任意請求頭)。簡單請求時,浏覽器會根據此響應頭的内容決定是否給腳本傳回相應内容,預先驗證請求時,浏覽器會根據此響應頭決定是否發送實際的跨域請求。

繼續閱讀