摘要:簡單地理解就是因為同源策略的限制,它是瀏覽器為了安全性考慮一種非常重要的策略,域名下的無法操作或是域名下的對象。同源策略會限制以下三種行為和無法讀取。例如中可以引用等資源,此類操作不受同源策略限制。
同源策略
同源策略same origin policy中的重要內(nèi)容就是URL(uniform resource locator),統(tǒng)一資源定位符,俗稱網(wǎng)址。URL中的resource資源就是css,js,html,img等內(nèi)容。
源包括當前頁面的域名、協(xié)議、端口號。http協(xié)議默認端口是80,https協(xié)議默認端口是443。同源策略是瀏覽器的一個安全機制功能,Same Origin Policy,同源就是當協(xié)議、域名、端口號一致時就是同源。不同源的客戶端腳本在沒有明確授權(quán)下,不能讀寫對方的資源。簡單地理解就是因為同源策略的限制,它是瀏覽器為了安全性考慮一種非常重要的策略,a.com 域名下的js無法操作b.com或是c.a.com域名下的對象。更詳細的說明可以看下表:
瀏覽器基于用戶的隱私安全目的,防止惡意網(wǎng)站竊取數(shù)據(jù)(只是瀏覽器有這個同源策略設(shè)置,但是用命令行curl請求某個跨域地址時能得到相應(yīng)結(jié)果),不允許不同域名的網(wǎng)站之間互相調(diào)用ajax XHR對象,只是不允許XHR對象,對其他的圖片、js腳本、css腳本還是可以通過標簽跨域調(diào)用。所以css/js/img可以跨域請求(即引用),AJAX不能請求跨域的資源。
curl http://www.abc.com 用這個命令獲得了http://www.abc.com指向的頁面,同樣,如果這里的URL指向的是一個文件或者一幅圖都可以直接下載到本地。如果下載的是HTML文檔,那么缺省的將不顯示文件頭部,即HTML文檔的header。要全部顯示,請加參數(shù) -i,要只顯示頭部,用參數(shù) -I。任何時候,可以使用 -v 命令看curl是怎樣工作的,它向服務(wù)器發(fā)送的所有命令都會顯示出來。為了斷點續(xù)傳,可以使用-r參數(shù)來指定傳輸范圍。
【1】 Cookie、LocalStorage 和 IndexedDB 無法讀取。Cookie 是服務(wù)器寫入瀏覽器的一小段信息,只有同源的網(wǎng)頁才能共享。
【2】 DOM 無法獲得。
【3】 AJAX 請求無效(可以發(fā)送,但瀏覽器會拒絕接受響應(yīng))。
主域名 abc.com
www.abc.com //www是子域名
bbs.abc.com //bbs是子域名
beijing.bbs.abc.com //beijing.bbs是子域名
haidian.beijing.bbs.abc.com //haidian.beijing.bbs是子域名
主域名是不帶www的域名,例如a.com,主域名前面帶前綴的通常都為二級域名或多級域名,例如www.a.com其實是二級域名。
解決跨域問題的兩個前提特別注意:
第一,如果是協(xié)議和端口造成的跨域問題“前端”無法解決。
第二:在跨域問題上,域僅僅是通過“URL的首部”來識別而不會去嘗試判斷相同的ip地址對應(yīng)著兩個域或兩個域是否在同一個ip上。比如在host文件中可將兩個不同域名綁定到同一個IP地址上形成跨域。
“URL的首部”可在console.log控制臺中用window.location.方法名獲取
跨域就是用某種方法突破同源策略的限制,實現(xiàn)獲取其他域中的資源。實現(xiàn)跨域一般有四種方法:
1.JSONP實現(xiàn)跨域JSONP(json with padding)方式, 通過script標簽請求資源,允許用戶在scr地址中傳遞一個callback參數(shù)(callback=abc)即將預(yù)先定義好的回調(diào)函數(shù)名以查詢字符串的形式傳遞給服務(wù)端,服務(wù)端收到請求后會將要返回的數(shù)據(jù)用這個callback參數(shù)(abc)包裹住再返回,即將請求傳入的參數(shù)abc作為函數(shù)名來包裹住要返回的JSON數(shù)據(jù),比如abc(JSON),這樣客戶端在收到服務(wù)端返回的abc(JSON)文件后默認用JS解析執(zhí)行。通過JSONP就可以隨意定制自己的函數(shù)來自動處理返回數(shù)據(jù)了。
jsonp的原理:雖然瀏覽器默認禁止了跨域訪問,但并不禁止在頁面中script標簽引用其他域下的JS文件,比如線上jquery庫,并可以自由執(zhí)行引入的JS文件中的function(包括操作cookie、Dom等等)。根據(jù)這一點,可以方便地通過動態(tài)創(chuàng)建script節(jié)點的方法來實現(xiàn)完全跨域的通信。例如a.com/index.html中可以引用b.com/main.js、b.com/style.css、b.com/logo.png等資源,此類操作不受同源策略限制。實際操作中如果在a.com下用ajax去請求(讀寫)b.com下的內(nèi)容會被同源策略阻止,但a.com里如果引用了b.com/main.js,雖然可以引用,但當這個引用的js文件(在a.com下引用)去讀寫(ajax)b.com的資源時一樣會提示ajax錯誤。注意讀寫和引用有本質(zhì)區(qū)別的,受同源策略限制ajax不能(POST寫/GET讀)請求跨域內(nèi)容,但可以通過script引用的方式獲取目標域上js文件,如果在這個被引用的JS文件內(nèi)存放數(shù)據(jù),這樣就能從目標域獲取到數(shù)據(jù)了,這就是JSONP實現(xiàn)的原理。
JSONP與JSON沒有關(guān)系。JSON是規(guī)定語法的一種字符串的寫法。JSONP(json with padding),這里的padding就是被請求的目標域B域返回的數(shù)據(jù)其外層包裹的A域預(yù)先定義好的函數(shù)+括號。JSONP就是動態(tài)的script,即A域前臺傳什么callback=abc給B域名后臺,B域就生成對應(yīng)的abc方法,這個方法的執(zhí)行過程是A域預(yù)先定義好的
jsonp的缺點:
1.安全問題,src引用是開放的,所以jsonp的資源都被所有人訪問到。解決方法是用jsonp中的token參數(shù),通過A域和B域共用同一套cookie來驗證A的身份。
2.只能用GET方式不能用POST方式獲取數(shù)據(jù)即只能讀不能寫,因為是基于scr引用的,引用是get請求。
3.可被注入惡意代碼如?callback=alert(1); 這問題只能用正則過濾字符串的方法解決,過濾callback后的內(nèi)容不能有括號之類的條件
JSONP的實現(xiàn)代碼如下:
1.定義數(shù)據(jù)處理函數(shù)
appendHtml(){ xxxxx }
2.創(chuàng)建script標簽,src的地址執(zhí)行后端接口,最后加個參數(shù)callback=appendHtml.如:
var script=document.createElement("script") script.src="http://127.0.0.1/getNews?callback=appendHtml"
3.服務(wù)端在收到請求后,解析參數(shù),計算返還數(shù)據(jù),輸出 appendHtml(data) 字符串。
4.前臺頁面收到服務(wù)端返回的appendHtml(data)字符串所構(gòu)建的script標簽,頁面加載這個script標簽時做為js執(zhí)行。此時會調(diào)用appendHtml()函數(shù),將data做為參數(shù)。
注意:JSONP實現(xiàn)的前提是后端必須有JSONP的API接口,即后端有將前端傳入的參數(shù)作為函數(shù)名包裹數(shù)據(jù)返回js文件的邏輯。如:
var data=[{"a":1,"b":2}] var cb=req.query.callback; if(cb){ res.send(`${cb}(JSON.stringify(${data}))`) }else{ res.send(`${data}`) }
需后臺配合,代碼如下圖
function jsonp(url,_callback){ var scriptNode=document.createElement("script") scriptNode.setAttribute("type","text/javascript") scriptNode.setAttribute("src",`${url}?callback=${_callback}`) document.head.appendChild(scriptNode) } function getMusic(content){ /*定義數(shù)據(jù)返回時所執(zhí)行的回調(diào)函數(shù)*/ console.log(content.song[0]) var comeMusic=content.song[0], musicTitle=comeMusic.title, musicArtist=comeMusic.artist, imgSrc=comeMusic.picture, _src=imgSrc.match(/.[^@]*/g)[0] /*正則匹配返回的圖片地址*/ console.log(_src) imgNode.src=_src h3Node.innerText=musicTitle h6Node.innerText=musicArtist }
調(diào)用jsonp函數(shù):
jsonp(http://www.aaa.com,getMusic)
CORS允許瀏覽器向跨域服務(wù)器發(fā)出XMLHttpRequest請求,突破了AJAX只能同源使用的限制。CORS需要瀏覽器和服務(wù)器同時支持,目前,所有瀏覽器都支持該功能,IE瀏覽器不能低于IE10。CORS原理:瀏覽器一旦發(fā)現(xiàn)AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。只要服務(wù)器實現(xiàn)了CORS接口,就可以跨源通信。
以nodejs server-mock后臺為例:
res.header("Accept-Control-Allow-Origin","http://www.a.com:8080")//只允許http://www.a.com:8080這個源發(fā)起的請求
res.header("Accept-Control-Allow-Origin","*")//允許所有源發(fā)起的請求
瀏覽器將跨域請求分為兩類
簡單請求(simple request),同時滿足以下兩個條件
【1】請求方法是HEAD/GET/POST之一
【2】HTTP請求頭信息不超出以下幾種字段
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
非簡單請求(not-so-simple request),不滿足以上兩個條件的請求就是非簡單請求
AJAX發(fā)起跨域請求時,如果是簡單類型請求,請求頭信息如下:
GET /cors HTTP/1.1
Origin: http://api.bob.com //發(fā)起此次請求的所在源(協(xié)議+域名+端口號)
Host: api.alice.com //要訪問的目標域
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
如果Origin指定的源,不在服務(wù)端許可范圍內(nèi),服務(wù)器會返回一個正常的HTTP回應(yīng)。但這個響應(yīng)頭信息沒有包含Access-Control-Allow-Origin字段。瀏覽器接收后就知道出錯了,從而拋出一個錯誤,被XMLHttpRequest的onerror回調(diào)函數(shù)捕獲。這種錯誤無法通過狀態(tài)碼識別,因為HTTP回應(yīng)的狀態(tài)碼有可能是200。
如果Origin指定的域名在許可范圍內(nèi),服務(wù)器返回的響應(yīng),響應(yīng)頭會包含以下信息字段
Access-Control-Allow-Origin: http://api.bob.com //允許來自源http://api.bob.com的訪問,如果是*則代表允許來自所有源的訪問
Access-Control-Allow-Credentials: true //是否允許客戶端在請求中發(fā)送Cookie
Access-Control-Expose-Headers: FooBar //允許CORS請求拿到除默認的Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma外的其他字段。這里允許拿到foobar字段信息。
Content-Type: text/html; charset=utf-8
CORS請求默認不包含Cookie信息,如果需要包含Cookie信息,一方面要服務(wù)器同意
Access-Control-Allow-Credentials: true //但Access-Control-Allow-origin 不能是*
另一方面,開發(fā)者必須在AJAX請求中打開withCredentials屬性。
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
此時Cookie依然遵循同源政策,只有用服務(wù)器域名設(shè)置的Cookie才會上傳,其他域名的Cookie并不會上傳,且(跨源)原網(wǎng)頁代碼中的document.cookie也無法讀取服務(wù)器域名下的Cookie。
AJAX發(fā)起跨域請求時,如果是非簡單類型請求,比如請求方法是PUT或DELETE,或者Content-Type字段的類型是application/json,非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱為“預(yù)檢”請求,瀏覽器先詢問服務(wù)器,當前網(wǎng)頁所在的域名是否在服務(wù)器的許可名單之中,以及可以使用哪些HTTP動詞和頭信息字段。只有得到肯定答復(fù),瀏覽器才會發(fā)出正式的XMLHttpRequest請求,否則就報錯。
比如js代碼如下:
var url = "http://api.alice.com/cors";
var xhr = new XMLHttpRequest();
xhr.open("PUT", url, true); //方法是PUT
xhr.setRequestHeader("X-Custom-Header", "value"); //自定義請求頭信息
xhr.send();
請求頭信息如下:
OPTIONS /cors HTTP/1.1 //方法是OPTIONS,表示這個請求是用來詢問的。
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
如果服務(wù)器否定了“預(yù)檢”請求,會返回一個正常的HTTP回應(yīng),但是沒有任何CORS相關(guān)的頭信息字段。瀏覽器接收后就知道出錯了,被XMLHttpRequest對象的onerror回調(diào)函數(shù)捕獲。
如果服務(wù)器接受了“預(yù)檢”請求,以后每次瀏覽器正常的CORS請求,就都跟簡單請求一樣,會有一個Origin頭信息字段。服務(wù)器的回應(yīng),也都會有一個Access-Control-Allow-Origin頭信息字段。服務(wù)器發(fā)送響應(yīng)頭信息如下:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT //表明服務(wù)器支持的所有跨域請求的方法
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
JSONP只支持GET請求,CORS支持所有類型的HTTP請求。JSONP的優(yōu)勢在于支持老式瀏覽器,以及可以向不支持CORS的網(wǎng)站請求數(shù)據(jù)。
3.HTML5跨文檔通信API window.postMessageHTML5中最酷的新功能之一就是 跨文檔消息傳輸Cross Document Messaging。下一代瀏覽器都將支持這個功能:Chrome 2.0+、Internet Explorer 8.0+, Firefox 3.0+, Opera 9.6+, 和 Safari 4.0+ 。 Facebook已經(jīng)使用了這個功能,用postMessage支持基于web的實時消息傳遞。
舉例,父窗口aaa.com向子窗口bbb.com發(fā)消息,調(diào)用postMessage方法如下:
var popup = window.open("http://bbb.com", "title"); popup.postMessage("Hello World!", "http://bbb.com");
postMessage方法的第一個參數(shù)是具體的信息內(nèi)容(支持任意類型),第二個參數(shù)是接收消息目標窗口的源origin(協(xié)議 + 域名 + 端口)也可以設(shè)為*,表示不限制域名,向所有窗口發(fā)送。
子窗口向父窗口發(fā)送消息的寫法如下:
window.opener.postMessage("Nice to see you", "http://aaa.com");
父窗口和子窗口都可以通過message事件,監(jiān)聽對方的消息:
window.addEventListener("message", function(event) { console.log(e.data); },false);
事件對象event的三個屬性:
event.source:發(fā)送消息的窗口對象,對發(fā)送消息的窗口對象的引用;
event.origin: 發(fā)送消息的窗口的源(協(xié)議、域名、端口號)這里不是接受消息的窗口的源
event.data: 消息內(nèi)容
event.origin屬性可以過濾不是發(fā)給本窗口的消息,舉例如下:
當bbb網(wǎng)站收到來自aaa網(wǎng)站發(fā)來的消息
window.addEventListener("message", receiveMessage); function receiveMessage(event) { if (event.origin !== "http://aaa.com") return;//判斷發(fā)消息的窗口的源是否是aaa網(wǎng)站的源,這里的event.origin和postMessage()方法中的origin不一樣??! if (event.data === "Hello World") { event.source.postMessage("Hello", event.origin);//這里event.origin指向aaa網(wǎng)站的源即消息接收的窗口的源 } else { console.log(event.data); } }
otherWindow.postMessage方法中,otherWindow是其他窗口的一個引用,比如iframe的contentWindow屬性、執(zhí)行window.open返回的窗口對象、或者是命名過或數(shù)值索引的window.frames。即只能適用于open方法打開的頁面相互發(fā)送消息,或者通過iframe嵌套的頁面之間發(fā)送消息。通過window.postMessage()方法還可讀取其他窗口的LocalStorage
window.open("http://www.bbb.com:8881/b_index.html","title") //主頁面中打開子頁面
window.opener.postMessage(${msg},"http://www.aaa.com:8888") //引用打開子頁面的主頁面
當兩個網(wǎng)頁一級域名相同,只是二級域名不同,瀏覽器允許通過設(shè)置document.domain來實現(xiàn)iframe窗口相互訪問或設(shè)置cookie。降域方式只適用于同一站點下不同子域名共享cookie或者iframe頁面中嵌套的子域名頁面之間的訪問。降域不適用訪問LocalStorage 和 IndexedDB,postMessage()方法可訪問LocalStorage
用改寫document.domain+iframe的方法來獲取目標域數(shù)據(jù)。缺點是安全性差,一個頁面被攻擊后另一個頁面的數(shù)據(jù)也會被泄露且不支持ajax方式請求數(shù)據(jù)。降域只能解決主域相同而二級域名(子域名)不同的兩個頁面請求數(shù)據(jù)的情況,如果把script.a.com的domian設(shè)為alibaba.com那顯然是會報錯。domain只能設(shè)置為主域名,不可以在b.a.com中將domain設(shè)置為c.a.com;且只能由子域名改到父域名或父父域名,不能從父父域名改到子域名(比如將b.com改成script.b.com是不行的)
舉例來說,A網(wǎng)頁是http://w1.example.com/a.html,B網(wǎng)頁是http://w2.example.com/b.html,子域名不同默認情況下會被同源策略阻止訪問。只要將兩個頁面都設(shè)置相同的document.domain,兩個網(wǎng)頁就可以共享Cookie或在iframe窗口下相互訪問。
document.domain = "example.com";
服務(wù)器也可以在設(shè)置 Cookie 的時候,指定 Cookie 的所屬域名為一級域名以后二級域名和三級域名不用做任何設(shè)置,都可以讀取這個Cookie。比如.example.com。
Set-Cookie: key=value; domain=.example.com; path=/
降域的特點:只能對主域相同子域不同的iframe頁面和父頁面(或者open方法打開的主域相同的頁面)形式的跨域起作用,可實現(xiàn)的兩個頁面共享cookie。修改domain時top頁面和iframe頁面都要使用document.domain去修改成一致的域名。具體的做法是可以在http://www.a.com/a.html和http://script.a.com/b.html兩個文件中分別加上document.domain = "a.com";然后通過a.html文件中創(chuàng)建一個iframe,去控制iframe的contentDocument,這樣兩個js文件之間就可以“交互”了。驗證步驟如下:
1.本地文件夾中有兩個文件index.html(www.a.com下的網(wǎng)頁),b.html(script.a.com下的網(wǎng)頁),a.com下的index.html頁面中有iframe頁面(src="http://script.a.com:8080/b.html")
2.由于域僅僅是通過“URL的首部”來識別,不會判斷兩個不同域名是否為同一IP地址。根據(jù)這點,修改本地host文件增加兩行,子域名不同,主域名相同來模擬跨域。
127.0.0.1 www.a.com
127.0.0.1 script.a.com
3.用mock start命令啟動服務(wù)器并分別訪問
http://www.a.com:8080/index.html
http://script.a.com:8080/b.html
兩個頁面console.log(document.domain)時分別返回www.a.com和script.a.com,此時是跨域狀態(tài)。
4.當在www.a.com下的html頁面中執(zhí)行以下腳本時提示
var ccc=document.getElementsByTagName("iframe")[0].contentDocument
(注:.contentDocument方法可操作iframe頁面內(nèi)的信息)
5.解決方案:www.a.com下的index.html頁面和script.a.com下的b.html頁面中都增加腳本,將兩個頁面的域設(shè)置成相同的主域名
再次執(zhí)行var ccc=document.getElementsByTagName("iframe")[0].contentDocument
返回的就是b.html文件中的信息
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/108121.html
摘要:可以說同源策略在安全中扮演著及其重要的角色。我把這個領(lǐng)域的東西寫成了一個系列,以后還會繼續(xù)完善下去安全一同源策略與跨域安全二攻擊安全三攻擊 之所以要將同源策略與跨域?qū)懺谝黄?,是因為存在瀏覽器的同源策略,才會存在跨域問題 何為同源策略 同源策略是瀏覽器實現(xiàn)的一種安全策略,它限制了不同源之間的文檔和腳本交互的權(quán)限。只有同一個源的腳本才會具有操作dom、讀寫cookie、session 、a...
摘要:由于瀏覽器的同源策略導(dǎo)致無法直接通過拿到后臺數(shù)據(jù)。目前,如果非同源,共有三種行為受到限制。此處應(yīng)有掌聲參考關(guān)于跨域資源共享和瀏覽器的同源策略限制的具體講解。 工作中,經(jīng)常會遇到需要跨域請求數(shù)據(jù)的情況。由于瀏覽器的同源策略,導(dǎo)致無法直接通過ajax拿到后臺數(shù)據(jù)。解決這個問題的方式之一就是JSONP。還有一種方式更高效簡單——跨域資源共享(Cross-origin Resource Sha...
摘要:扯了這么多,自然不是為了吹水,而是要為了引出前端開發(fā)的一個重要的知識點同源策略什么是同源策略出于保護用戶信息安全的目的,現(xiàn)在的瀏覽器都會實施同源策略這個政策,所謂同源策略指的是不同源的客戶端腳本在沒有明確授權(quán)情況下,不允許讀寫對方的資源。 導(dǎo)語你家的小孩帶了他的朋友來你們的家里玩,你家的小孩如果要在自家屋里拿玩具玩、拿東西吃你自然是不會阻止,但是如果你家小孩的朋友人品不行,亂拿東西吃、...
摘要:扯了這么多,自然不是為了吹水,而是要為了引出前端開發(fā)的一個重要的知識點同源策略什么是同源策略出于保護用戶信息安全的目的,現(xiàn)在的瀏覽器都會實施同源策略這個政策,所謂同源策略指的是不同源的客戶端腳本在沒有明確授權(quán)情況下,不允許讀寫對方的資源。 導(dǎo)語你家的小孩帶了他的朋友來你們的家里玩,你家的小孩如果要在自家屋里拿玩具玩、拿東西吃你自然是不會阻止,但是如果你家小孩的朋友人品不行,亂拿東西吃、...
摘要:方案瀏覽器設(shè)置一級域名。場景完全不同源的網(wǎng)站,需要窗口通信方案父子窗口互相寫互相監(jiān)聽子窗口寫后跳回同域父窗口讀瀏覽器跨文檔通信場景請求非同源地址方案架設(shè)服務(wù)器代理參考資料瀏覽器同源政策及其規(guī)避方法阮一峰前端常見跨域解決方案全跨域幾種方式 同源 概念:協(xié)議,域名,端口 相同。目的:保證用戶信息的安全,防止惡意的網(wǎng)站竊取數(shù)據(jù)。限制的行為: Cookie、LocalStorage 和 In...
摘要:方案瀏覽器設(shè)置一級域名。場景完全不同源的網(wǎng)站,需要窗口通信方案父子窗口互相寫互相監(jiān)聽子窗口寫后跳回同域父窗口讀瀏覽器跨文檔通信場景請求非同源地址方案架設(shè)服務(wù)器代理參考資料瀏覽器同源政策及其規(guī)避方法阮一峰前端常見跨域解決方案全跨域幾種方式 同源 概念:協(xié)議,域名,端口 相同。目的:保證用戶信息的安全,防止惡意的網(wǎng)站竊取數(shù)據(jù)。限制的行為: Cookie、LocalStorage 和 In...
閱讀 2154·2021-11-11 16:54
閱讀 1119·2021-10-12 10:12
閱讀 445·2019-08-30 15:43
閱讀 722·2019-08-29 13:15
閱讀 1148·2019-08-29 13:12
閱讀 1600·2019-08-26 12:09
閱讀 1728·2019-08-26 10:24
閱讀 2351·2019-08-26 10:15