看后端程序员调试CORS的姿势
# 目錄
為什么有同源策略?
- 需要解決的問題
CORS跨域請求方案
- preflight
- withCredentials
- 附:高效、優雅地調試CORS實現
為什么有同源策略??
同源策略Same Origin Policy 是一種約定,是瀏覽器最核心的安全功能:?該策略允許在源自同一站點的頁面上運行的腳本在沒有特定限制的情況下訪問彼此的數據,但阻止腳本訪問存儲在不同域中的數據。
所謂同源是指域名、協議、端口相同。
同源策略主要是為了安全,如果瀏覽器沒有同源限制,那么:
? ? ? ? ① 某域下的Cookie等與該域相關的密切數據可以任意讀取
? ? ? ? ② 不同域下DOM任意操作,篡改
? ? ? ? ③?惡意網站能隨意執行Ajax腳本偷取隱私數據
針對以上第③點:舉個栗子:
用戶正在訪問銀行網站B但未注銷。然后用戶轉到另一個站點A,該站點在后臺運行一些惡意JavaScript代碼,該代碼從銀行站點B請求數據。由于用戶仍然登錄銀行站點,惡意代碼可以執行用戶在銀行站點B上執行的任何操作。
例如,它可以獲得用戶上次交易的列表,創建新交易等。這是因為瀏覽器可以基于銀行站點的域向銀行站點發送和接收會話cookie。
訪問惡意站點的用戶以為他訪問的站點無法訪問銀行會話cookie。雖然惡意網站下JavaScript確實無法直接訪問銀行會話cookie,但它仍然可以通過銀行網站的會話cookie向銀行網站發送和接收請求。
同源策略在實施中需要解決的問題:
同源策略限制 通過腳本訪問不同域的數據,大棒一揮, 關閉了正常訪問外部域名數據的可能。 有以下變通方法:-
實現CORS (Cross-Origin Resource Sharing)
-
使用JSONP (JSON Padding)
-
Use?postMessage?method
-
建立一個本地代理服務器,這樣先同源訪問,由代理服務器轉發請求
?
CORS跨域請求方案
CORS是一個服務器允許放松同源策略的W3C標準,服務器可以顯式允許同源請求并且拒絕其他的非同源訪問。
CORS標準描述了新的HTTP標頭,它為瀏覽器提供了一種僅在獲得許可時才能請求遠程URL的方法。盡管服務器可以執行某些驗證和授權,但瀏覽器通常負責支持這些標頭并遵守它們所施加的限制。
Request headers- Origin
- Access-Control-Request-Method
- Access-Control-Request-Headers
- Access-Control-Allow-Origin
- Access-Control-Allow-Credentials
- Access-Control-Expose-Headers
- Access-Control-Max-Age
- Access-Control-Allow-Methods
- Access-Control-Allow-Headers
舉個橘子:
CORS規范? ? ? ? ?① 瀏覽器使用XmlHttpRequest發起跨域Ajax請求,瀏覽器會自動攜帶Origin請求
? ? ? ? ?② 服務器有一套自己的的CORS邏輯,這個邏輯會在服務端響應頭:Access-Control--*******--中體現
? ? ? ? ? ? ? ? ?舉例: Access-Control-Allow-Origin: */Origin/null // 該響應頭會3種可能值
? ? ? ? ?③ 瀏覽器會遵守Access-Control--*******-- 響應頭對應值所施加的限制
GET /resources/public-data/ HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Connection: keep-alive Referer: http://foo.example/examples/access-control/simpleXSInvocation.html Origin: http://foo.exampleHTTP/1.1 200 OK Date: Mon, 01 Dec 2008 00:23:53 GMT Server: Apache/2.0.61 Access-Control-Allow-Origin: * Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: application/xmlBODY?
預檢Preflight
? ? ? ? 對于可以修改數據的Ajax和HTTP請求方法(通常是GET以外的HTTP方法,或者某些MIME類型的POST用法),CORS規范要求瀏覽器“預檢”請求,
使用HTTP OPTIONS請求方法從服務器請求支持的方法(由瀏覽器發起),然后,在服務器“批準”時,使用實際的HTTP請求方法發送實際請求。
上圖描述了瀏覽器是是怎樣決定使用【簡單請求】還是 【預檢XHR請求】
const invocation = new XMLHttpRequest(); const url = 'http://bar.other/resources/post-here/'; const body = '<?xml version="1.0"?><person><name>Arun</name></person>';function callOtherDomain(){if(invocation){invocation.open('POST', url, true);invocation.setRequestHeader('X-PINGOTHER', 'pingpong');invocation.setRequestHeader('Content-Type', 'application/xml');invocation.onreadystatechange = handler;invocation.send(body); } }...... // 以上使用POST 發送了一個xml,同時自定義了一個request header: X-PINGOTHER, 該跨域請求必定會觸發瀏覽器預檢行為?
?
攜帶憑據跨域
? ? ?默認情況下,瀏覽器在XMLHttpRequest請求中不會發送憑據 (憑據包括HTTP cookies 和 Http認證信息比如 Authorization頭)。
?訪問的外站API需要授權,這時就涉及攜帶憑據跨域:
const invocation = new XMLHttpRequest(); const url = 'http://bar.other/resources/credentialed-content/';function callOtherDomain(){if(invocation) {invocation.open('GET', url, true);invocation.withCredentials = true; // 該標記設定在XMLHttpRequest中發送憑據invocation.onreadystatechange = handler;invocation.send(); } }?
GET /resources/access-control-with-credentials/ HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Connection: keep-alive Referer: http://foo.example/examples/credential.html Origin: http://foo.example Cookie: pageAccess=2HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:34:52 GMT Server: Apache/2.0.61 (Unix) PHP/4.4.7 mod_ssl/2.0.61 OpenSSL/0.9.7e mod_fastcgi/2.4.2 DAV/2 SVN/1.4.2 X-Powered-By: PHP/5.2.6 Access-Control-Allow-Origin: http://foo.example // 當響應的是一個攜帶憑據的請求,服務端必須為Access-Control-Allow-Origin響應頭指定一個Origin,而不能再用 * 通配符。 Access-Control-Allow-Credentials: true Cache-Control: no-cache Pragma: no-cache Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT // 設置新的Cookie Vary: Accept-Encoding, Origin Content-Encoding: gzip Content-Length: 106 Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: text/plain[text/plain payload]需要注意:
以上攜帶請求憑據跨域,若響應頭Access-Control-Allow-Origin被指定為“*”, 請求將報錯;?也就是說 在攜帶憑據跨域的時候,服務器響應頭不允許使用囫圇吞棗的* 通配符。
雖然瀏覽器提示報錯;實際上瀏覽器會取得Response,但是瀏覽器會遵守CORS規范阻止你的代碼訪問響應體。
?
附:一種高效、優雅地調試CORS實現的方法
https://stackoverflow.com/questions/12173990/how-can-you-debug-a-cors-request-with-curl/12179364#12179364CORS規范很大程度體現了服務端對資源的安全策略, 作為后端開發人員,調試CORS規范不是一個簡單的事情:
直觀上:需要搭建跨域服務器、構建AJAX腳本請求。
使用Curl命令來調試CORS規范, 好嗨喲。 Curl是一個利用URl規則在命令行下工作的文件傳輸工具,可以說是一款強大的http命令行工具。 https://curl.haxx.se/docs/manpage.html 下面curl命令演示了在某網站跨域Post請求外站api時 觸發的預檢options請求: curl "https://A.com/api/3.0/drilldown/query" -X OPTIONS -H "Access-Control-Request-Method: POST" -H "Origin: http://www.example.com.cn" -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36" -H "Access-Control-Request-Headers: content-type" --verbose-X 請求動詞
-H 指定請求頭
--verbose 輸出詳細內容
結果如下:?
以上從為什么?怎么做?更高效的完成? 三方面講述了CORS,希望對大家有所幫助。?
?
作者:JulianHuang感謝您的認真閱讀,如有問題請大膽斧正;覺得有用,請下方或加關注。
本文歡迎轉載,但請保留此段聲明,且在文章頁面明顯位置注明本文的作者及原文鏈接。
轉載于:https://www.cnblogs.com/JulianHuang/p/10337980.html
總結
以上是生活随笔為你收集整理的看后端程序员调试CORS的姿势的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用kubeadm安装kubernete
- 下一篇: HashMap和Hashtable 线程