同源策略(Same-Origin-Policy,SOP)javascript
同源策略是一種約定,是瀏覽器最核心也最基本的一個安全功能,不一樣源的客戶端腳本在沒有明確受權的狀況下,不能讀寫對方資源。好比a.com下的js腳本採用ajax讀取b.com裏面的文件數據是會報錯的。若是缺乏了同源策略,則瀏覽器的正常功能均可能會受到影響。能夠說Web是構建在同源策略基礎上的,瀏覽器只是針對同源策略的一種實現。html
同源策略限制了從同一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的重要安全機制。前端
爲何要有同源策略java
瀏覽器主要是從兩個方面去作同源策略的,一是針對接口的請求,二是針對DOM的查詢。ajax
接口請求上的同源策略json
有一個小小的東西叫cookie你們都知道,通常是用來處理登錄等場景,目的是讓服務端知道請求是誰發出的。若是你請求了接口進行登錄,服務端驗證經過以後就會在響應頭(Response Headers)加入Set-Cookie屬性,而後下次再發請求的時候,瀏覽器就會自動將cookie附加在HTTP請求頭(Request Headers)的cookie屬性中,服務端就能知道這個用戶已經登錄過了。後端
知道了cookie的工做原理後,咱們來看沒有同源策略的場景:api
1.你準備去剁手,因而打開了買買買網站,登錄,選了一些東西到購物車。跨域
2.你還沒選夠東西呢,忽然彈出來一個連接,你一點,跳到了騙騙騙網站。瀏覽器
3.騙騙騙網站吸引住你了,你饒有興趣地瀏覽着,殊不知道這個網站在暗地裏作了壞事:它向買買買網站發起了請求!由於你登錄和瀏覽買買買網站的時候已經產生了cookie在瀏覽器中,那騙騙騙網站由於沒有同源策略的限制,就能夠偷偷拿到這個cookie,就至關於登錄了你買買買網站的帳號,而後隨心所欲,偷偷地給你買一堆面膜。
這就是傳說中的CSRF(Cross-site Request Rorgery,跨站請求僞造)攻擊。
DOM查詢上的同源策略
有一天你收到一條短信,說是你的銀行帳號有風險,要你趕忙點進www.yinghang.com更改密碼。你嚇尿了,卡里有三十多塊錢呢,因而你果斷點進去,看到仍是熟悉的銀行登錄界面,果斷地輸入你的帳號密碼。着急的你卻沒有看清楚,其實平時訪問的是www.yinhang.com。那麼問題來了,這個釣魚網站究竟會作什麼呢?
首先,它會在網頁中內嵌一個iframe,iframe中是真的www.yinhang.com。
<iframe name="yinhang" src="www.yinhang.com"></iframe>
而後,由於沒有同源策略限制,它就能拿到www.yinhang.com網站的DOM。
window.frames['yinhang'].document.getElementById('你輸入帳號密碼的input').val();
經過DOM去獲取輸入的帳號密碼,而後取走你卡里的三十多塊錢,讓你痛不欲生。
綜上所述,同源策略是能有效規避一些風險的。但不是說有了同源策略就必定安全的,只是說同源策略是瀏覽器的一種最基本的安全機制,畢竟能提升一點攻擊成本。事實上,沒有刺不穿的盾,只有攻擊的成本和攻擊成功後得到收益的正比。
源(Origin)
咱們知道,URL(統一資源定位符)是由協議、域名、端口和路徑組成的。一般理解的源,就是指的是URL中的協議、域名和端口的組合。
既然URL是描述資源的,那麼源也是描述資源的一個部分。
這樣理解,靜靜是個來自山西的女孩子,芳芳是個來自廣東的女孩子。描述靜靜的時候,就會說她是來自山西的靜靜;描述芳芳的時候,就會說她是來自廣東的芳芳。這個來自哪裏,就是源。
同源(Same-Origin)
若是兩個URL的協議、域名和端口相同,則稱它們是同源的。
莉莉也是來自廣東的女孩子,這時就能夠說她和來自廣東的芳芳是同源的。
跨域(Cross-Origin)
與同源相反的,只要協議、域名和端口三個組成中有任何一個不一樣,就視爲不一樣源。
從一個源(預)去請求不一樣源(域)的資源,就稱爲跨域。
瀏覽器中有一些不受同源限制的標籤,好比說<script>、<img>、<iframe>、<link>這些包含src屬性的標籤能夠加載跨域的資源。可是瀏覽器限制了JavaScript的權限使其不能讀或寫這些標籤內加載的內容。
如何解開JavaScript的限制去讀、寫這些標籤內加載的內容,實際上就是咱們要解決的跨域問題。
跨域的解決方案
跨域主要有幾個解決方案:降域、postMessage、JSONP、CORS和反向代理等。
同源策略限制下接口請求的跨域解決方案:JSONP、CORS和反向代理等。
JSONP
前面提到,在HTML標籤裏一些標籤,好比說<script>、<img>這樣帶src屬性的獲取資源的標籤,是沒有跨域限制的。利用這一點,咱們就能夠乾點壞事,呸,好事。
如今有個a.com/jsonp.html想要獲得b.com/main.js中的數據,就能夠在a.com的jsonp.html裏面建立一個回調函數callback1,動態地添加<script>元素,而後向服務器發送請求,在請求地址後面加上查詢字符串,最後經過callback參數指定回調函數的名字。
這時的請求地址就是b.com/main.ja?callback=callback1。在main.js中調用這個回調函數callback1,而且以JSON數據的形式做爲參數傳遞,完成回調。咱們來看看代碼:
/* a.com/jsonp.html中的代碼 */ // 建立script標籤 function addScriptTag(src) { var script = document.createElement('script'); script.setAttribute("type", "text/javascript"); script.src = src; document.body.appendChild(script); } // 頁面加載完成後再執行 window.onload = function () { addScriptTag("http://b.com/main.js?callback=foo"); } // 回調函數 function foo(data) { console.log(data.name + "好可愛"); };
/* b.com/main.js中的代碼 */ foo({name:"靜靜"}); // 調用回調函數,把要的東西傳給它
這樣,就完成了跨域的參數傳遞。
JSONP的注意事項:
1.JSONP和JSON看起來很類似,可是其實他們之間沒有半毛錢關係。
2.使用這種方式的話,只要是個網站都能拿到b.com裏的數據,存在安全性問題,須要網站雙方商議基礎token的身份驗證。
3.JSONP只能是GET的方式,不支持POST等其它方式。
4.JSONP可能在回調函數中被注入惡意代碼,篡改頁面內容。能夠採用字符串過濾的方法規避此問題。
5.jQuery或AngularJs中有提供封裝好的jsonp方法。
CORS(CrossOrigin Resource Sharing, 跨源資源共享)
CORS是一個W3C標準,看名字就知道這時處理跨域問題的標準作法。CORS有兩種請求,簡單請求(Simple Request)和非簡單請求(Not-so-simple Request)。
簡單請求
同時知足如下兩個條件,就屬於簡單請求:
1.請求方法必須是HEAD、GET、POST三種方法之一。
2.HTTP的頭信息不能多於Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(只限application/x-www-form-urlencoded、mltipart/form-data、text/plain三個值)這些字段。
非簡單請求
非簡單請求會發出一次預檢測請求,返回碼是204,預檢測經過以後纔會發真正的請求,這才返回200。能夠經過前端發請求的時候增長一個額外的headers來觸發非簡單請求。
CORS的實現方式比較複雜,要用另外的篇幅去敘述,這裏就不作敘述了(懶,哈哈哈哈)。
反向代理
試想一下,若是咱們請求的時候仍是用前端的域名,可是當提交到後臺的時候,有個東西幫咱們把這個請求轉發到真正的後端域名上,不就避免跨域了嗎?當前很火的Nginx就是一個能解決跨域問題的反向代理應用。
Nginx的配置
server{ # 監聽9009端口 listen 9009; # 域名是localhost server_name localhost; # 凡是localhost:9009/api這個樣子的,都轉發到真正的服務端地址localhost:9888上去 location ^~ /api { proxy_pass http://localhost:9888; } }
前端的請求
http://localhost:9009/api/dosomething
前端請求的時候直接使用前端這邊的域名localhost:9009便可,這樣就是同源了,不會有跨域的問題。Nginx監聽到凡是localhost:9009/api這樣的,都轉發到真正的服務端地址localhost:9888上。
Nginx轉發請求的方式彷佛很方便,可是這種使用是要看場景的。好比說若是後端接口是一個公共服務的API,去獲取天氣什麼的,前端調用的時候總不能讓運維去配Nginx吧。若是瀏覽器兼容性沒問題的話(IE10或以上),CORS纔是更通用的作法。
同源策略限制下DOM查詢的跨域解決方案:降域、postMessage等。
降域(Descending Domain)
同源策略認爲域和子域屬於不一樣的域。好比說:
child1.parent.com與parent.com不一樣源
child1.parent.com與child2.parent.com不一樣源
grandson1.child1.parent與child1.parent.com不一樣源
能夠經過設置document.domain = 'parent.com'讓瀏覽器認爲他們都屬於同一個源。想要實現以上任意兩個頁面之間的通訊,兩個頁面都必須這樣設置。
降域的特色與注意事項:
1.降域主要針對於Cookie和當前頁面下的iframe。
2.降域只能在父域名和子域名之間使用,即只適合在同一主域名下使用。且將grandson1.child1.parent.com的document.domain設置爲parent.com以後,不能再設置成child1.parent.com。
3.降域須要有降的空間,頂級域名沒法降域。
4.降域是要雙向設置document.domain的,不只當前頁面要設置,iframe內嵌的頁面也要設置。
5.降域存在安全性問題,當一個站點被攻擊後,另外一個站點也會引發安全漏洞。
postMessage
window.postMessage()是HTML5的一個接口,專一實現不一樣窗口不一樣頁面的跨域通信。
具體使用這裏就不作敘述了(仍是懶,嘿嘿)。
"別說了,我想靜靜。"
"靜靜是誰?"