跨域的實(shí)現(xiàn)方式有哪些?
Web場(chǎng)景中包含了腳本執(zhí)行功能的用戶代理(典型的是瀏覽器),對(duì)跨域訪問實(shí)施了安全限制(遵循同源策略),導(dǎo)致我們?cè)陂_發(fā)中碰到跨域訪問時(shí)需要使用特定機(jī)制來實(shí)現(xiàn)。
跨域的域這里的域,不是單純的域名。IETF的RFC6454標(biāo)準(zhǔn)(2011年12月正式發(fā)布)說明了源(Origin)的概念。源是一組URI的集合,而這一組URI的特征是完全符合一個(gè)三元組(uri-scheme,uri-host,uri-port)。我們所說的跨域訪問,實(shí)際上是跨源訪問。后續(xù)回答中的描述中域和源可能混用。
依據(jù)三元組很容易劃分哪些URI同源,哪些不同源。下面兩張出自RFC6454的示例列表(第一張3個(gè)同源,第二張都不同源):
協(xié)議標(biāo)準(zhǔn)將scheme、host、port都作為源的分組條件,是基于以下考慮的:
1、不同的協(xié)議一般會(huì)實(shí)施不同的安全策略,比如https通過TSL加密傳輸數(shù)據(jù)提高安全性。
2、父子域名配合端口,在稍大的平臺(tái)中其實(shí)都是分開部署(這里自然還含架構(gòu)設(shè)計(jì)等),且有不同安全級(jí)別的要求。比如京東登錄使用的是passport.jd.com子域名,很多子系統(tǒng)的登錄入口都在這里。
IE比較調(diào)皮,未將端口加入分組要素,也就是不同端口并不跨源(第二張圖片前兩個(gè)URI在IE在就是同源),同時(shí)對(duì)于域也有自己安全區(qū)域的一套機(jī)制。
跨域的安全問題Web領(lǐng)域的用戶代理,實(shí)際包括瀏覽器、僅實(shí)現(xiàn)HTTP功能的編程組件,爬蟲工具等。瀏覽器需要渲染界面(html、css)、執(zhí)行腳本(js),同時(shí)瀏覽器是一個(gè)公共環(huán)境,我們可以用它瀏覽來自全世界Web服務(wù)器提供的資源信息。因?yàn)橛辛四_本執(zhí)行能力(包括js以及各種插件擴(kuò)展),如果不加限制自然會(huì)引發(fā)了諸多的安全問題??缬蚍矫娴陌踩珕栴}主要包括跨站腳本(XXS)、跨站點(diǎn)請(qǐng)求偽造(CSRF)等。下圖示owasp發(fā)布的2017年10大威脅,詳細(xì)信息可以上owasp官網(wǎng)查看。
跨域的安全問題主要是基于同源策略(Same-origin policy)來限制。
簡(jiǎn)單來說同源策略主要保護(hù)是某一個(gè)源相關(guān)的js對(duì)象以及其它可訪問資源(比如cookie、本地存儲(chǔ)等)。比如:很多服務(wù)器基于 cookie信息來發(fā)布敏感信息或采取狀態(tài)改變操作。必須在客戶端維護(hù)由不相關(guān)站點(diǎn)提供的內(nèi)容之間的嚴(yán)格分離,以防止數(shù)據(jù)機(jī)密性或完整性的丟失。
跨域訪問策略用戶代理對(duì)跨源訪問會(huì)做諸多限制,但同時(shí)也會(huì)提供跨源訪問的機(jī)制。服務(wù)端也會(huì)有限制客戶端跨域訪問的需求,典型比如的盜鏈問題。所以跨域訪問需要清楚服務(wù)端和客戶端各自的限制和需求。我主要分下面兩個(gè)場(chǎng)景來描述可用策略。
跨源本地訪問
因?yàn)閒rame的存在,我們可以在同一頁面包含不同源的資源;我們也可以在新窗口中打開不同源的頁面。
我們可以通過跨文檔消息來實(shí)現(xiàn)交互:通過window對(duì)象異步調(diào)用postMessage方法,在另一個(gè)窗口觸發(fā)onmessage事件。window對(duì)象可以通過多種方式獲取到引用,比如iframe的contentWindow、window.open返回的window對(duì)象、或者在window.frames中查找。
跨源訪問不同服務(wù)器資源
可用策略多種多樣,我把他們歸為以下幾類:
利用漏洞:如Jsonp,這個(gè)和XXS有異曲同工之妙。瀏覽器下載并執(zhí)行非同源js是必要的,但是存在安全隱患,所以不要亂引用三方腳本。
插件擴(kuò)展:通過flash,silverlight等插件擴(kuò)展避過瀏覽器的直接限制,充當(dāng)客戶端代理層。補(bǔ)充協(xié)議:跨域資源共享(CORS)目前由WHATWG維護(hù)。本質(zhì)上是客戶端和服務(wù)端協(xié)商機(jī)制。詳情可參考fetch文檔或者M(jìn)DN文檔說明。換協(xié)議:Websocket不使用同源策略,但瀏覽器提供Origin頭,服務(wù)端可以維護(hù)白名單來進(jìn)行跨域限制。服務(wù)端代理:通過nginx之類的代理服務(wù)器將不同源的信息轉(zhuǎn)化為同源信息,客戶端就不存在跨域問題了。也可以自己實(shí)現(xiàn)一個(gè)代理組件,這里依賴的就是HTTP組件并未提供嚴(yán)苛的同源策略實(shí)現(xiàn),你甚至可以根據(jù)需要修改Origin頭來欺騙對(duì)方服務(wù)器。父子域名:可通過更改document.domain來臨時(shí)放寬同源限制。如果你思維夠抽象,你會(huì)發(fā)現(xiàn)跨域訪問和IPC機(jī)制很相似,只要你想得透徹,方式方法很多。
Tips:MDN是個(gè)不錯(cuò)的Web開發(fā)參考網(wǎng)站,由mozilla維護(hù),可用來做知識(shí)點(diǎn)補(bǔ)充(深度不太夠,但是包含范圍很廣)和API參考。IE的官方文檔關(guān)于API的參考也是重定向到這里,其實(shí)想想也挺搞笑的,實(shí)現(xiàn)走非主流路線,文檔倒是走到一起了。
總結(jié)跨域背后是安全性問題,包含有還有非常多的規(guī)范和實(shí)現(xiàn)細(xì)節(jié),比如安全上下文、源的繼承等等。與跨域訪問需求相對(duì)的,我們有時(shí)還需要非常嚴(yán)格的跨域訪問限制,防止各種安全問題出現(xiàn)。
跨域訪問需求很廣泛,以后可能還會(huì)出更多的規(guī)范放寬同源限制,原則應(yīng)該都是需要客戶端和服務(wù)端預(yù)先了解到風(fēng)險(xiǎn)并確認(rèn)風(fēng)險(xiǎn),跟APP應(yīng)用開發(fā)的授權(quán)類似。不過在安全性和便利性之間的取舍不是那么容易的。
個(gè)人手敲,歡迎批評(píng)指正。