CORS為什么能保障安全?為什么只對復雜請求做預檢?
大家好,我是年年!提起CORS,大部分的文章都在寫什么是簡單請求、什么是復雜請求,復雜請求預檢的流程又是怎樣。
但如果問你:
- CORS為什么要帶上源,這是為了保障當前站點的安全還是目的服務器的安全?
- 為什么區分簡單請求和復雜請求,只對復雜請求做預檢?
這篇文章會圍繞CORS是如何保障安全的的,講清這幾個問題。讀完可以對CORS知其然,并知其所以然。
什么是CORS
相信每個前端的控制臺都中都被打印過這樣一段話,告訴你:你的跨域請求策略攔截啦!
首先要明確的一點,CORS的目的不是攔截請求,反倒是為了讓其能正常請求。
CORS誕生的背景是「同源策略」。這是一個相當嚴苛的規定,它禁止了跨域的AJAX請求。但實際的開發中又有這樣的需求,于是開一個口子——只要配置了CORS的對應規則,跨域請求就能正常進行。這也正和CORS的名字對應起來了——「跨域資源共享」,就是為了能讓跨域請求在「同源策略」的大背景下進行。
回到上面提到控制臺報錯,這不是阻止你做跨域請求,而是提示你:因為沒有按照CORS要求做配置,不得不暫時攔截。
怎樣配置CORS
上文講清了,只要按照CORS要求做配置,就能突破同源策略的限制,下面將會講述如何配置。
這部分不需要前端操心,完全后端來做:在響應頭里面加一個字段Access-Control-Allow-Origin(允許請求的來源),這個值要把前端的源包含進去。
舉個例子:請求的后端接口是http://fe_nian,你本地正在開發前端工程跑在8080端口。那么后端會在響應頭里加上Access-Control-Allow-Origin:*來允許http://localhost:8080這個源去做跨域請求,因為*是所有的意思。
跨域請求的流程
CORS把請求分成簡單請求和復雜請求,劃分的依據是“是否會產生副作用”。
簡單貼一下定義,同時滿足下面這兩個條件的是簡單請求
- 請求方法是HEAD/GET/POST。
- 請求體的文件類型只能是form-urlencoded、form-data、text/plain(這類文章很多,不再贅述,可以看阮一峰-跨域資源共享)。
對于簡單請求,流程如下:
- 瀏覽器發起請求,并且自動加上請求的來源origin給服務器檢查。
- 服務器返回數據,并返回檢查結果,配置CORS響應頭。
- 瀏覽器檢查CORS響應頭,如果包含了當前的源則放行,反之攔截。
這里需要注意,瀏覽器是攔截響應,而不是攔截請求,跨域請求是發出去的,并且服務端做了響應,只是瀏覽器攔截了下來。
對于復雜請求,整個流程如下:
- 瀏覽器發起預檢請求,帶上請求的來源origin,不包含請求體。
- 服務器返回檢查結果,配置CORS頭。
- 瀏覽器發起真正請求。
- 瀏覽器返回數據。
瀏覽器會檢查第2步中拿到的CORS頭,如果沒有包含當前的源,后續的第3、4步都不會進行,也就是不會發起真正請求。
為什么要帶上源
CORS給開發帶來了便利,同時也帶來了安全隱患——CSRF攻擊。
它的基本流程如下:
- 用戶登錄受害網站,把獲取的身份憑證保存在瀏覽器的cookie中。也就是上圖流程的①②③。
- 用戶用同一瀏覽器打開黑客網站,黑客網站向受害網站服務器發起一個惡意請求,這時瀏覽器會自動從cookie中取出身份憑證,把它帶上。也就是上圖的④⑤。
- 受害網站服務端發現有身份憑證,惡意請求被成功受理。
如果嚴格按照同源政策,第2步的跨域請求不能進行的,也就不會造成危害。所以CORS策略的心智模型是:所有跨域請求都是不安全的,瀏覽器要帶上來源給服務器檢驗。
如果做過服務端開發,應該知道,服務端不存在跨域一說,去獲取另一個服務器的資源是再順暢不過的事情。因為服務端不像瀏覽器一樣,作為“容器”存貯著用戶身份憑證——也就是上面的第1步發生的事情,它去做跨域請求沒有這樣的風險。
為什么只對復雜請求做預檢
上文提到,劃分簡單請求和復雜請求的依據是“是否產生副作用”。這里的副作用指對數據庫做出修改:使用GET請求獲取新聞列表,數據庫中的記錄不會做出改變,而使用PUT請求去修改一條記錄,數據庫中的記錄就發生了改變。
對于簡單請求,瀏覽器只會在請求頭加上一個origin字段標識請求來源;對于非簡單請求,瀏覽器會先發出一個預檢請求,獲得肯定回答后才會發送真正的請求,下面會講清楚為什么這么做。
可以假設網站被CSRF攻擊了——黑客網站向銀行的服務器發起跨域請求,并且這個銀行的安全意識很弱,只要有登錄憑證cookie就可以成功響應:
黑客網站發起一個GET請求,目的是查看受害用戶本月的賬單。銀行的服務器會返回正確的數據,不過影響并不大,而且由于瀏覽器的攔截,最后黑客也沒有拿到這份數據;
黑客網站發起一個PUT請求,目的是把受害用戶的賬戶余額清零。瀏覽器會首先做一次預檢,發現收到的響應并沒有帶上CORS響應頭,于是真正的PUT請求不會發出;
幸好有預檢機制,否則PUT請求一旦發出,黑客的攻擊就成功了。
結語
回到開頭的兩個問題,不難得出答案:
- 對于跨域請求帶上請求來源,是為了防止CSRF攻擊;瀏覽器的心智模型是:跨域請求都是不安全的,CORS的機制是為了保障請求目的服務器的安全。
- 依據是否對服務器有副作用,劃分了簡單請求和復雜請求(但由于歷史原因,表單POST請求也被劃分成了簡單請求),預檢機制會把不安全的復雜請求攔截下來,避免對服務器造成危害,而簡單請求通常不會對服務器的資源作出修改,即使發出危害不大。