web安全一,同源策略與跨域

之因此要將同源策略與跨域寫在一塊兒,是由於存在瀏覽器的同源策略,纔會存在跨域問題

何爲同源策略

同源策略是瀏覽器實現的一種安全策略,它限制了不一樣源之間的文檔和腳本交互的權限。只有同一個源的腳本纔會具備操做dom、讀寫cookie、session 、ajax等敏感操做的權限。能夠說同源策略在web安全中扮演着及其重要的角色。所謂同源簡單來講便是兩個頁面必須具備相同的協議、端口還有域名。

http://www.site.com/index.html爲例,舉個小栗子:html

url 是否跨域 緣由
http://www.site.com/other/ind...
http://child.site.com/index.html origin 不同(www與child)
http://www.site.com:8090/index.html 端口 不同(80與8090)
https://www.site.com/index.html 協議 不同(http與https)

IE是個例外

不得不調侃一下,在兼容性方面IE彷佛永遠都難以跟上別的瀏覽器發展的步伐,永遠都是一個例外。前端

  • 授信範圍(Trust Zones):兩個相互之間高度互信的域名,如公司域名(corporate domains),不遵照同源策略的限制。
  • 端口:IE未將端口號加入到同源策略的組成部分之中,所以 http://www.size.com:81/index.html 和http://www.size.com:8090/index.html 屬於同源而且不受任何限制。

雖然同源策略限制了跨域文檔腳本的操做能力,但明確容許部分資源性的標籤是能夠加載跨域資源的,如<img>,<script>部分具備跨域能力的標籤以下,來源於MDNhtml5

  • <script src="..."></script> 標籤嵌入跨域腳本。語法錯誤信息只能在同源腳本中捕捉到。
  • <link rel="stylesheet" href="...">標籤嵌入CSS。因爲CSS的鬆散的語法規則,CSS的跨域須要一個設置正確的Content-Type消息頭。
  • <img>嵌入圖片。
  • <video> 和 <audio>嵌入多媒體資源。
  • <object>, <embed> 和 <applet>的插件。
  • @font-face引入的字體。
  • <frame> 和 <iframe>載入的任何資源。站點可使用X-Frame-Options消息頭來阻止這種形式的跨域交互。

跨域的方法

在瀏覽器同源策略的安全限制下,跨域的需求仍然是有的。這種需求能夠分爲正當的和不正當的。具體來講不正當的就是,第三方惡意網站利用同源策略的一些漏洞,對目標網站進行一些不容許的操做,這種操做咱們稱之爲跨域攻擊。具體能夠參照個人另外幾篇博客:jquery

  • CSRF攻擊(跨域僞請求)
  • XSS攻擊(跨域腳本攻擊)

正當的跨域請求能夠是: 一些大集團下面的幾個子域名之間的跨域交互,也能夠是合做網站之間的跨域交互等等,總之就是被容許的。下面來具體說說實現這種正當跨域的幾種方法:git

寫在前面: 全部的跨域方法都會帶來安全性的問題,一旦惡意網站可以成功在目標網站運行腳本或者具備操做的能力,他們也就具備了目標網站的跨域能力。

document.domain

腳本能夠將 document.domain 的值設置爲其當前域或其當前域的超級域。github

這種方法的優勢就是:簡單,只要在前端js中設置就行了
弊端:只適合用於子域與父域之間還有子域之間的跨域web

CORS / Access-Control-Allow-Origin: *

實現起來異常簡單,主要是經過在服務器設置HttpHeader,客戶端檢查本身的域是否在容許列表中,決定是否處理響應。詳情請參考CORS MDN, 利用CORS實現跨域請求ajax

服務器端能夠在HTTP的響應頭中加入(頁面層次的控制模式):
Access-Control-Allow-Origin: example.com
Access-Control-Request-Method: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization, Accept, Range, Origin
Access-Control-Expose-Headers: Content-Range
Access-Control-Max-Age: 3600json

多個域名之間用逗號分隔,表示對所示域名提供跨域訪問權限。"*"表示容許全部域名的跨域訪問。segmentfault

此方法的優勢:簡單快捷,配置簡單,基本能夠解決任何域的跨域問題
弊端:僅適用與客戶端與跨域服務器的交互;另外具備瀏覽器的兼容性問題,不兼容IE7以及如下。
在IE8——IE9 中要使用xhr = new XDomainRequest();來替代xhr = new XMLHttpRequest();

查看跨域腳本的錯誤

在之前,當嘗試使用 window.onerror 去記錄跨域腳本的錯誤時,只會返回 Script error, 無法獲取到具體的報錯信息。這種狀況在html5中獲得了改觀,html新加了一個crossorigin屬性,容許捕獲跨域腳本的詳細錯誤信息,可是有兩個條件:

  1. 跨域腳本的服務器必須經過 Access-Controll-Allow-Origin 頭信息容許當前域名能夠獲取錯誤信息
  2. 當前域名的 script 標籤也必須指明 src 屬性指定的地址是支持跨域的地址,也就是要添加crossorigin 屬性

crossorigin 具備兩個值:
anonymous: 請求不帶 credentials flag .
use-credentials: 請求攜帶 credentials flag .

credentials flag 也就是常說的cookies, client-side SSL certificates

window.postMessage

postMessage是HTML5新的語法特性,可讓跨域的腳本之間進行交互,主要分爲消息的發送端和消息的接收端。
發送端:
otherWindow是其餘窗口的一個引用,好比iframe的contentWindow屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames。

otherWindow.postMessage(
message, // 要發送到其餘窗體的消息,能夠傳送任何數據對象
targetOrigin, // 消息要發送到的域,由完整的協議、地址、端口組成
[transfer] // 這是一個可選的抽象接口
);

otherWindow.postMessage() 方法被調用時,會在全部頁面腳本執行完畢以後(e.g., 在該方法以後設置的事件、以前設置的timeout 事件,etc.)向目標窗口派發一個 MessageEvent 消息。
接收端:

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
  // For Chrome, the origin property is in the event.originalEvent
  // object.
  var origin = event.origin || event.originalEvent.origin; 
  if (origin !== "http://example.org:8080")
    return;

  // ...
}

message 事件,包含4個參數

  • message 屬性表示該message 的類型;
  • data 屬性爲 window.postMessage 的第一個參數即message;

— origin 調用window.postMessage() URI
— source 調用window.postMessage() 的窗口對象

兼容性:在 Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3)以前, 參數 message 必須是一個字符串。而IE10下面也有奇怪的問題

優勢:適合同一個瀏覽器兩個跨域頁面之間的腳本交互
弊端:只能從窗口的引用(也就是window.open或者window.frames[0].contentWindow)發送消息到目標窗口,適用的範圍有限

JSONP

jsonp 是比較古老的跨域方法,也是比較經常使用的跨域
具體來講,它的實現以下:

function jsonp(url,data,callbackName) {
var script = document.createElement('script');
var src = url+ '?'+ data+ 'calback='+callbackName;
script.src = src;
document.head.appendChild(script);
}

function callbackName(data){
    // data from server;
}

JSONP的坑

由於在jquery中JSONP使用的是ajax的封裝接口,不少人它會是ajax的一種技術,實際上它是利用<script>標籤天生具備跨域能力的一種技術,因此不少人在jquery中使用JSONP時都會踩坑。

  • 坑一:同步,不少人會想固然地使用ajax的同步參數async來實現同步。實際上jsonp只有異步,由於script是異步加載的
  • 坑二:錯誤回調,在使用jquery的過程,不少人會使用ajax的errorfail接口,來捕獲跨域的異常返回。ajax的錯誤接口只能捕獲請求超時、終止、json解析錯誤等正常的錯誤,可是對於網絡不通、服務器異常,jquery是選擇靜默地失敗的。

那怎麼捕獲jsonp的異常呢?
實際上<script>標籤在遇到這些錯誤時會觸發error事件。因此上面的代碼咱們能夠稍微改一下,增長一個捕獲異常的回調

function jsonp(url,data,callbackName,errorbackName) {
var script = document.createElement('script');
var src = url+ '?'+ data+ 'calback='+callbackName;
script.src = src;
script.onerror = errorbackName;
document.head.appendChild(script);
}

function callbackName(data){
    // data from server;
}

另外在jquery中的處理主要有兩種方法,參考 jQuery使用JSONP時的錯誤處理

  • 手動暴露jsonp 的錯誤接口
var head = document.head || $('head')[0] || document.documentElement; // code from jquery
var script = $(head).find('script')[0];
script.onerror(function(evt) {
    alert('error');
});
  • 使用jsonp的拓展庫

針對jquery處理jsonp的坑,早有大神封裝好插件,直接引用就能夠了,方便快捷省心。傳送門

【相關】
web安全,是一個很重要的技能,也是一個領域的知識。我把這個領域的東西寫成了一個系列,之後還會繼續完善下去:
web安全一:同源策略與跨域
web安全二:CSRF 攻擊
web安全三:XSS 攻擊

相關文章
相關標籤/搜索