跨源限制
在瀏覽器里
對源(url協議,主機名,端口號)不同的通信進行限制,在web領域 為了確保安全,只有同源的通信才能被允許進行,稱為同源策略
雖然可以在html里使用iframe以實現在一個頁面中同時顯示來自不同域的文檔,不過js仍然只能訪問同一源的文檔
如果文檔的url和iframe的不同,則無法通過文檔中包含的js對iframe內的dom進行操作,而iframe內的js也無法操作文檔里的dom
如果不這樣 ,就會發生諸如不同域的cookie能夠互相訪問等安全問題
對於XMLHttpRequest來說,同源策略的含義是,一個XMLHttpRequest對象只能發送至一個特定的服務器,即提供了使用該XMLHttpRequest對象的文檔下載服務器
跨源通信
js里的跨源通信
JSONP
iframe攻擊
window.postMessage()
XMLHttpRequest level 2
1.JSONP
利用script標簽的src將其它域的js文件載入,如果在此處動態創建script標簽的話,就能實現對其它域中數據的動態讀取
不過僅僅是取得了數據,還無法在客戶端使用。因此產生jsonp(json with padding ,padding指向json數據中添加函數名)
服務端會對數據添加函數名后返回
代碼示例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script> </head> <body> <script> var url="http://b.cn/do.php?callback=foo"; //注意 foo函數的定義要先於 引入 url里的內容 function foo(res){ console.log(res); console.log(res.a); } function loadData(url){ var elem=document.createElement('script'); elem.src=url; document.getElementsByTagName('head')[0].appendChild(elem); } loadData(url); </script> </body> </html>
b.cn/do.php
<?php $arr=['a'=>'ajax','b'=>'bbc']; $callback=$_GET['callback']; $json=$callback."(".json_encode($arr).");"; echo $json; //foo({"a":"ajax","b":"bbc"}); ?>
請求的內容是執行代碼的內容,在客戶端是定義聲明部分
jsonp的缺點
無法在post請求類型中使用
只能做到動態創建script標簽並讀取數據而無法從客戶端發出數據
2. XMLHttpRequest level 2
XMLHttpRequest無法跨源通信
在XMLHttpRequest level2中 只要服務器端許可就可以實現
a.cn/a.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script> </head> <body> <div class="btn" style="cursor: pointer;text-align:center;width: 100px;height: 50px;line-height:50px;color:white;background: green">click</div> <script> $(function(){ $(".btn").click(function(){ var xhr=new XMLHttpRequest(); xhr.onreadystatechange=function(){ var state=xhr.readyState; if(state==4){ console.log(xhr.getAllResponseHeaders()); } } xhr.open('GET','http://b.cn/do.php?a=1&b=2'); xhr.withCredentials=true; //默認不發送cookie的,當設個參數設置為true服務端 Access-Control-Allow-Origin 必須明確指定 不可為* 而且必須設置 header('Access-Control-Allow-Credentials:true'); xhr.setRequestHeader("content-type",'multipart/form-data'); // xhr.setRequestHeader("content-type",'application/x-www-form-urlencoded'); xhr.send('p1=1'); }); }); </script> </body> </html>
http://b.cn/do.php
代碼
<?php header('Access-Control-Allow-Origin:http://a.cn'); header('Access-Control-Allow-Credentials:true'); header('Access-Control-Allow-Methods:POST'); var_dump($_POST); var_dump($_GET); var_dump(file_get_contents("php://input")); var_dump($_COOKIE);
關於跨源發送cookie
xhr.withCredentials=true;
這里跨源指的是 跨的二級的域,根域必須相同,而且cookie的域必須是根域
比如 a.a.cn可以通過這里所說的跨源 在向b.a.cn發送ajax請求時 帶上 .a.cn下的cookie
不要以為 域在.a.cn下的cookie ,a.a.cn在向b.a.cn發請求時 會自動帶上
也不要認為cookie的跨源能跨根域,是不可能的
3.postMessage()
window.postMessage
是一個安全的跨源通信的方法。一般情況下,當且僅當執行腳本的頁面使用相同的協議(通常都是 http)、相同的端口(http默認使用80端口)和相同的 host(兩個頁面的document.domain 的值相同)時,才允許不同頁面上的腳本互相訪問。 window.postMessage
提供了一個可控的機制來安全地繞過這一限制,當其在正確使用的情況下。
調用 window.postMessage時,
將分發一個
MessageEvent
事件到目標窗口, 在所有掛起必須執行的腳本完成后. (例如:當一個事件處理程序調用window.postMessage時,仍剩余事件處理程序, 先前的掛起等待超時等)。MessageEvent 有消息類型,它被設置為第一個參數值提供給window.postMessage的data屬性,
對應的window調用window.postMessage的時候,window.postMessage主文檔的來源的origin屬性被稱為源屬性,指哪個調用window.postMessage的窗口。 (事件的其他標准屬性都存在與對應的預期值。)
語法
otherWindow.postMessage(message, targetOrigin);
-
otherWindow
- 其他窗口的一個引用,比如iframe的contentWindow屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames
-
message
- 將要發送到其他 window的數據,將會被結構化克隆算法序列化。這意味着你可不受什么限制的安全傳送數據對象給目標窗口而無需自己序列化
-
targetOrigin
-
通過窗口的origin屬性來指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示無限制)或者一個URI。在發送消息的時候,如果目標窗口的協議、主機地址或端口這三者的任意一項不匹配targetOrigin提供的值,那么消息就不會被發送;只有三者完全匹配,消息才會被發送。這個機制用來控制消息可以發送到哪些窗口;例如,當用
postMessage傳送密碼時,這個參數就顯得尤為重要,必須保證它的值與這條包含密碼的信息的預期接受者的orign屬性完全一致,來防止密碼被惡意的第三方截獲。如果你明確的知道消息應該發送到哪個窗口,那么請始終提供一個有確切值的targetOrigin,而不是*。不提供確切的目標將導致數據泄露到任何對數據感興趣的惡意站點 。
a.cn/a.html
<!DOCTYPE html> <html> <head> <title>Post Message</title> </head> <body> <div style="width:200px; float:left; margin-right:200px;border:solid 1px #333;"> <div id="color">Frame Color</div> </div> <div> <iframe id="child" src="http://b.cn/b.html"></iframe> </div> <script type="text/javascript"> window.onload=function(){ window.frames[0].postMessage('getcolor','http://b.cn'); } window.addEventListener('message',function(e){ console.log('a:',e); var color=e.data; document.getElementById('color').style.backgroundColor=color; },false); </script> </body> </html>
b.cn/b.html
<!doctype html> <html> <head> <style type="text/css"> html,body{ height:100%; margin:0px; } </style> </head> <body style="height:100%;"> <div id="container" onclick="changeColor();" style="widht:100%; height:100%; background-color:rgb(204, 102, 0);"> click to change color </div> <script type="text/javascript"> var container=document.getElementById('container'); window.addEventListener('message',function(e){ if(e.source!=window.parent) return; var color=container.style.backgroundColor; window.parent.postMessage(color,'*'); },false); function changeColor () { var color=container.style.backgroundColor; if(color=='rgb(204, 102, 0)'){ color='rgb(204, 204, 0)'; }else{ color='rgb(204,102,0)'; } container.style.backgroundColor=color; window.parent.postMessage(color,'*'); } </script> </body> </html>