再也不學AJAX了!(三)跨域獲取資源 ① - 同源策略

libinfs發表於2017-12-04

我們之前提到過,AJAX技術使開發者能夠專注於網際網路中資料的傳輸,而不再拘泥於資料傳輸的載體。通過AJAX技術,我們獲取資料的方式變得更加靈活,可控和優雅。

但是AJAX技術並不是一把萬能鑰匙,網際網路中的資料隱私和資料安全(例如你的銀行賬號和密碼)也非常重要,為了保護某些使用者資料的隱私與安全,瀏覽器使用“同源策略”限制了AJAX技術獲取資料的範圍和能力。但在一些合理的場景中,我們又不得不想辦法繞過同源策略,實現跨域請求資源。因此“跨域技術”一直成為開發者們經久不衰的討論話題。

在“跨域獲取資源”這一主題中,我們將圍繞“同源策略”和“跨域”兩大主題展開,不但講述它們是什麼,更說明了為什麼要這麼做。相信你在讀完該主題下的兩篇文章後,一定會對這兩大主題有一個清晰,系統的認識。

需要提前宣告的是,本主題下的文章並不會像眾多相同主題的文章一樣羅列出所有的跨域技術,而只會撿最主流的四種進行講解。因為我並不打算寫“教你如何跨域”這樣型別的文章。

讓我們開始吧。

同源策略

整個網際網路世界的資料要麼儲存在服務端(即伺服器,如資料庫,硬碟等)中,要麼儲存在客戶端(即瀏覽器,如cookie,LocalStorage,sessionStorage)中。網際網路資料的傳輸實際上就是客戶端與服務端之間的互動。

而所謂的資料隱私與安全保護,說白了就是資料擁有者對資料索取者發出警告:“不是你的你別動”。

搞清了這個原則,我們就很容易明白,如果你在客戶端,並且想要獲取服務端資料,你首先需要通過伺服器端的驗證,證明你有許可權獲取資料(例如“登入”),而如果你在服務端,想要獲取客戶端的某些資料,你同樣需要客戶端通過某些方式驗證你有資格獲取相應的資料資源。

那麼上面提到的“某些方式”是什麼呢?其中最重要的就是我們今天的主題之一 -- 瀏覽器的“同源策略”。

瀏覽器的“同源策略”

瀏覽器所遵守的“同源策略”是指:限制不同源之間執行特定操作。這涉及到兩個問題:什麼是“”?,以及“特定操作”是指什麼?

讓我們停下來解釋一下這個概念:

  1. 一個協議域名三部分組成,這三者任一一個不同都會被瀏覽器識別為不同的源;
  2. 上文所提到的特定操作是指:
    • 讀取 Cookie,LocalStorage 和 IndexDB;
    • 獲取 DOM 元素;
    • 傳送 AJAX 請求;

在搞清了同源策略的概念之後,讓我們看看瀏覽器是出於怎樣的考慮,一直堅守著同源策略:

為什麼要有“源”的概念?

因為不同的源,大多數情況下就意味著它們在網際網路中歸屬於不同的站點(或是被用作不同的用途)。也就是說它們是不同的專案,有不同的檔案根目錄,那麼它們的資料也不應該共享也就理所應當了,否則資料的隱私和安全也無從談起。不過請注意,我上面所說的話是基於“不同源就彼此不相干”的假設,這其實存在一些問題,我們之後會提到。

為什麼不能執行“特定操作”?

這個需要我們假設,如果我們想做一些“壞事”,並且瀏覽器允許我們執行這些“特定操作”,我們作為“壞人”能做什麼:

首先,由於很多網站使用瀏覽器儲存使用者的使用者名稱和密碼,那麼我們便可以在A域中(我們在伺服器上託管的網站)讀取任意來訪使用者的所有Cookie資訊(沒有同源策略的保護,該使用者所有網站的Cookie記錄都是透明的),我們就可以利用這些Cookie資訊偽裝成來訪使用者做任何事,而在現實世界,出於同源政策的保護,我們只能訪問使用者該域下的Cookie資訊,也就是說,我們只能訪問我們自己設定的Cookie資訊。

其次,如果我們能夠獲取不同域下的DOM元素,我們就可以通過<iframe>標籤在我們的A域網站上引入B域網站,然後誘使使用者在B域網站操作,由於我們能夠跨域獲取DOM元素,因此我們可以操作B域網站的DOM結構,使用者輸入的一切資訊,以及使用者操作的DOM元素就都在我們的掌控之中了。這正是同源策略想要規避的安全隱患。

最後,為什麼要禁止不同源的站點傳送AJAX請求呢?這個說起來有些複雜,我們首先要對Cookie的運作原理有一個大致的瞭解:

當我們設定Cookie時,除了存放鍵值對形式的資料資訊外,瀏覽器還會為Cookie的一些屬性填充預設值(我們也可以手動修改這些屬性的值)。在這些屬性中,我們需要關注domainpath兩個屬性,它們一個代表域名,一個代表路徑,兩者加起來構成了一個確定這條Cookie何時被呼叫和訪問的URL。與此同時,瀏覽器自己維護的Cookie檔案中也會新增這一條新建立的Cookie資料。

當我們在瀏覽器中傳送HTTP請求時,瀏覽器首先會檢查請求地址並在自己所維護的Cookie檔案中尋找匹配的Cookie資訊,將其新增到請求頭中的Cookie屬性內,然後向伺服器傳送請求。請注意,這個自動新增相應Cookie資訊的過程是瀏覽器自己偷偷幫我們做到的,也就是說,我們自己無法控制這個過程。

下面重點來了,當我們的HTTP請求到達伺服器時,伺服器返回的響應中,響應頭會原封不動的返回我們傳送給他的Cookie資訊。嗅到危險的味道了嗎?我們雖然不能在傳送請求前獲得Cookie資訊,但是在傳送請求後,我們還是能夠獲得使用者的Cookie!

讓我再進一步解釋一下這和AJAX有什麼關係,假設我們在自己的伺服器上託管了站點A,並在其中隱藏了一段指令碼,每個登入站點A的人都會自動傳送AJAX請求至站點B(提示:站點B是一個銀行),那麼在沒有瀏覽器同源策略的情況下,如果站點A中的訪問者恰好有Cookie中保留站點B資訊的使用者,通過AJAX請求返回的響應頭,我們一樣可以拿到這位使用者的站點B Cookie,從而偽裝成使用者在站點B登入,做一些違法亂紀的事情(CSRF攻擊即是利用了這個原理,只不過出於同源策略限制,並不能通過發起AJAX的方式)。

雖然有些費勁,但是現在你應該明白為什麼同源策略要阻止跨域傳送AJAX了吧?(我終於將這個概念說清楚了,真是費了不少力氣 ?)

想要了解更多關於Cookie的資訊,可以參考這篇文章

同源策略的表現

看看我們的任務清單,我們解釋了什麼是同源策略以及瀏覽器為什麼要有同源策略,但至今為止,我們還不曾看到,在瀏覽器同源策略的限制下,當我們想要獲取跨域Cookie,DOM結構和傳送AJAX時,我們是如何被“拒絕”的。

首先,我們在一個域下只能讀取該域下的Cookie值。當我們在頁面中使用<iframe>標籤時,我們獲取對應DOM節點下只有一個空空的#document節點,並沒有額外的DOM資訊。

再也不學AJAX了!(三)跨域獲取資源 ① - 同源策略

而對於AJAX,瀏覽器其實並沒有阻止我們向不同域傳送請求,其阻止的是這次請求的響應,也就是說服務端其實接收到了這次請求,只是響應被瀏覽器解析時被瀏覽器發現違背了同源策略而被拒絕,此時,瀏覽器會在控制檯中列印出一條錯誤資訊。

再也不學AJAX了!(三)跨域獲取資源 ① - 同源策略

另外需要注意的是,對於XHR請求,實際上我們在請求報頭也不會看到相應的Cookie資訊,這是因為CORS標準中做了規定,預設情況下,瀏覽器在傳送跨域請求時,不能傳送任何認證資訊,比如cookiesHTTP authentication schemes。除非你顯式的將xhr例項的withCredentials屬性的值設定為true並且伺服器端也允許客戶端請求攜帶認證資訊(即伺服器端在響應頭中設定了Access-Control-Allow-Credentials: true)。

要在跨域請求頭中顯示Cookie真是不容易對吧?

但是,等等!CORS標準是什麼?

問得好,在下一篇以“跨域傳送請求”為主題的文章中,我們會詳細談到他。目前為止,你已經充分了解“同源策略”這個主題。Good Job!

現在讓我們先暫時休息一下,下一篇見 ?。




? Hey!喜歡這篇文章嗎?別忘了在下方? 點贊讓我知道。

相關文章