-
1 # 程式碼蟲蟲
-
2 # 遷徙de麻雀
Web場景中包含了指令碼執行功能的使用者代理(典型的是瀏覽器),對跨域訪問實施了安全限制(遵循同源策略),導致我們在開發中碰到跨域訪問時需要使用特定機制來實現。
跨域的域這裡的域,不是單純的域名。IETF的RFC6454標準(2011年12月正式釋出)說明了源(Origin)的概念。源是一組URI的集合,而這一組URI的特徵是完全符合一個三元組(uri-scheme,uri-host,uri-port)。我們所說的跨域訪問,實際上是跨源訪問。後續回答中的描述中域和源可能混用。
依據三元組很容易劃分哪些URI同源,哪些不同源。下面兩張出自RFC6454的示例列表(第一張3個同源,第二張都不同源):
協議標準將scheme、host、port都作為源的分組條件,是基於以下考慮的:
1、不同的協議一般會實施不同的安全策略,比如https透過TSL加密傳輸資料提高安全性。
2、父子域名配合埠,在稍大的平臺中其實都是分開部署(這裡自然還含架構設計等),且有不同安全級別的要求。比如京東登入使用的是passport.jd.com子域名,很多子系統的登入入口都在這裡。
IE比較調皮,未將埠加入分組要素,也就是不同埠並不跨源(第二張圖片前兩個URI在IE在就是同源),同時對於域也有自己安全區域的一套機制。
跨域的安全問題Web領域的使用者代理,實際包括瀏覽器、僅實現HTTP功能的程式設計元件,爬蟲工具等。瀏覽器需要渲染介面(html、css)、執行指令碼(js),同時瀏覽器是一個公共環境,我們可以用它瀏覽來自全世界Web伺服器提供的資源資訊。因為有了指令碼執行能力(包括js以及各種外掛擴充套件),如果不加限制自然會引發了諸多的安全問題。跨域方面的安全問題主要包括跨站指令碼(XXS)、跨站點請求偽造(CSRF)等。下圖示owasp釋出的2017年10大威脅,詳細資訊可以上owasp官網檢視。
跨域的安全問題主要是基於同源策略(Same-origin policy)來限制。
簡單來說同源策略主要保護是某一個源相關的js物件以及其它可訪問資源(比如cookie、本地儲存等)。比如:很多伺服器基於 cookie資訊來發布敏感資訊或採取狀態改變操作。必須在客戶端維護由不相關站點提供的內容之間的嚴格分離,以防止資料機密性或完整性的丟失。
跨域訪問策略使用者代理對跨源訪問會做諸多限制,但同時也會提供跨源訪問的機制。服務端也會有限制客戶端跨域訪問的需求,典型比如的盜鏈問題。所以跨域訪問需要清楚服務端和客戶端各自的限制和需求。我主要分下面兩個場景來描述可用策略。
跨源本地訪問
因為frame的存在,我們可以在同一頁面包含不同源的資源;我們也可以在新視窗中開啟不同源的頁面。
我們可以透過跨文件訊息來實現互動:透過window物件非同步呼叫postMessage方法,在另一個視窗觸發onmessage事件。window物件可以透過多種方式獲取到引用,比如iframe的contentWindow、window.open返回的window物件、或者在window.frames中查詢。
跨源訪問不同伺服器資源
可用策略多種多樣,我把他們歸為以下幾類:
利用漏洞:如Jsonp,這個和XXS有異曲同工之妙。瀏覽器下載並執行非同源js是必要的,但是存在安全隱患,所以不要亂引用三方指令碼。
外掛擴充套件:透過flash,silverlight等外掛擴充套件避過瀏覽器的直接限制,充當客戶端代理層。補充協議:跨域資源共享(CORS)目前由WHATWG維護。本質上是客戶端和服務端協商機制。詳情可參考fetch文件或者MDN文件說明。換協議:Websocket不使用同源策略,但瀏覽器提供Origin頭,服務端可以維護白名單來進行跨域限制。服務端代理:透過nginx之類的代理伺服器將不同源的資訊轉化為同源資訊,客戶端就不存在跨域問題了。也可以自己實現一個代理元件,這裡依賴的就是HTTP元件並未提供嚴苛的同源策略實現,你甚至可以根據需要修改Origin頭來欺騙對方伺服器。父子域名:可透過更改document.domain來臨時放寬同源限制。如果你思維夠抽象,你會發現跨域訪問和IPC機制很相似,只要你想得透徹,方式方法很多。
Tips:MDN是個不錯的Web開發參考網站,由mozilla維護,可用來做知識點補充(深度不太夠,但是包含範圍很廣)和API參考。IE的官方文件關於API的參考也是重定向到這裡,其實想想也挺搞笑的,實現走非主流路線,文件倒是走到一起了。
總結跨域背後是安全性問題,包含有還有非常多的規範和實現細節,比如安全上下文、源的繼承等等。與跨域訪問需求相對的,我們有時還需要非常嚴格的跨域訪問限制,防止各種安全問題出現。
跨域訪問需求很廣泛,以後可能還會出更多的規範放寬同源限制,原則應該都是需要客戶端和服務端預先了解到風險並確認風險,跟APP應用開發的授權類似。不過在安全性和便利性之間的取捨不是那麼容易的。
回覆列表
關於,跨域問題,我剛寫了一篇文章,現在就貼過來供給參考
大家有時經常問什麼是跨域問題,這個問題並不是一句話能回答的,因為我也不知道你說的是哪一種性質的跨域,比如說,你說的是不同域名的session共享呢?還是說ajax跨域問題呢。不管是哪種的跨域,我今天的文章中都給它列出來,並附上具體的操作方法,供大家借鑑,一起學習交流。
先說session共享的跨域問題,一個域名下都會保持一份會話,跨域就是說從一個域名到另一個域名這個會話還能繼續保持,很常見的就是在一個網站跳到另一個網站還是繼續保持登入狀態,一般情況下從一個域名下跳到另一個域名下,登入狀態是會丟失的,如果說沒有丟失,那麼就是進行了跨域處理。
圖片來之網際網路
關於跨域處理有兩個級別的處理:
1.兩個不同的二級域名session共享
這種級別的處理做個簡單的設定就可以了,一句程式碼就可以做到會話保持,我以php為例,假如說你是是個電商網站,一個是母嬰頻道,一個是數碼頻道,他們是兩個不同的二級域名,比如一個是muying.yourdomain.com,一個是dianzi.yourdomain.com,如果不做任何處理的話,從muying.yourdomain.com跳轉到dianzi.yourdomain.com登入狀態是沒法保持的,要想做到兩個二級域名的跨域就可以去修改php.ini的配置,修改改配置項session.cookie_domain,如果覺得修改配置項嫌麻煩的話,也可以修改php程式碼,在自己的php框架裡處理session的地方加上這麼一句就可以了
ini_set("session.cookie_domain","yourdomain.com");
有了這句程式碼之後關於yourdomain.com的所有二級域名的會話都能保持了,不會出現退出登入現象,就像同一個域名下訪問一樣。
圖片來之網際網路
2.兩個不同的域名的session共享
上面說的是同一個域名下的兩個不同二級域名的跨域問題,接下來要說的是兩個完全不一樣的域名的跨域問題,比如說一個域名是abc.com,另一個域名是cde.com,那麼這樣怎麼做到兩個不同的域名實現會話個共享呢?
先說一下原理,就是實現一個session的同步問題,在abc.com的頁面埋一個1畫素的圖片去請求一下cde.com並把session_id交給它,同樣在cde.com的頁面也埋一個1畫素的圖片去請求abc.com,並把session_id傳給伺服器,就這樣實現了2個域名之間的session共享問題
程式碼實現如下:
$currentHost = $_SERVER["HTTP_HOST"];$sessid = $_REQUEST["sid"];if(isset($sessid)){ session_id($sessid); session_start(); if(!isset($_SESSION["all_domains"])){ $_SESSION["all_domains"] = array( "abc.com" => false, "cde.com" => false, ); $_SESSION["all_domains"][$currentHost] = true; } else { $_SESSION["all_domains"][$currentHost] = true; } }else{ session_start(); if(!isset($_SESSION["all_domains"])){ $_SESSION["all_domains"] = array( "abc.com" => false, "cde.com" => false, ); $_SESSION["all_domains"][$currentHost] = true; } else { $_SESSION["all_domains"][$currentHost] = true; } }在其他php檔案中都包含以下這樣一句程式碼來保證session的同步<?phpforeach($_SESSION["all_domains"] as $domain => $domainset){ if(!$domainset){ echo "<img src="http://".$domain."/share.php?sid=".session_id()."" width="1" height="1"/>"; }}
圖片來之網際網路
3ajax的跨域問題
關於ajax的跨域問題有兩種方式去實現,一種是利用JSONP的原理,這個我就不過多說了,因網路上能搜的到,百度上搜JSONP有一堆,說的很詳細,JSONP的原理,及JSONP的具體實現等等,看了之後一會就學會了,
還有一種方式實現ajax的跨域,那就是在服務端的程式碼進行設定,也是一句程式碼的事情,就以PHP程式碼為例。在php的http請求框架中加入下面的這句程式碼,保證每一個請求能夠執行到就行。
header("Access-Control-Allow-Origin:*"); header("Access-Control-Allow-Methods:POST"); //這裡暫且設定請求型別為post header("Access-Control-Allow-Headers:x-requested-with,content-type");
加了這個程式碼之後,就可以實現ajax的跨域問題。