初探同源策略及其安全

今天了解了一些有關(guān)瀏覽器同源策略以及CSRF攻擊之后潮太,覺(jué)得挺有意思的,所以特此總結(jié)一波虾攻,給自己囤一點(diǎn)干貨
這篇文章主要包含三個(gè)大的方面:
1.同源策略是什么及其作用
2.如何繞過(guò)同源策略
3.CSEF攻擊及防御

同源策略及其安全.png

1.同源策略(same-origin policy):

1.1 什么叫做同源:

同源是針對(duì)域名來(lái)說(shuō)的铡买,對(duì)于任意的域名,只要他們的協(xié)議(HTTP,HTTPS)一致霎箍,主機(jī)一致奇钞,端口號(hào)一致,就可以將其認(rèn)為是同源的漂坏。

1.2 那么同源策略的作用是什么景埃?

** 說(shuō)到作用,那么就不可避免地要提及一下為什么會(huì)有這個(gè)策略的產(chǎn)生吧**

引用一段阮一峰大神的描述:

同源政策的目的顶别,是為了保證用戶信息的安全谷徙,防止惡意的網(wǎng)站竊取數(shù)據(jù)。
設(shè)想這樣一種情況:A網(wǎng)站是一家銀行筋夏,用戶登錄以后蒂胞,又去瀏覽其他網(wǎng)站。如果其他網(wǎng)站可以讀取A網(wǎng)站的 Cookie条篷,會(huì)發(fā)生什么骗随?
很顯然蛤织,如果 Cookie 包含隱私(比如存款總額),這些信息就會(huì)泄漏鸿染。更可怕的是指蚜,Cookie 往往用來(lái)保存用戶的登錄狀態(tài),如果用戶沒(méi)有退出登錄涨椒,其他網(wǎng)站就可以冒充用戶摊鸡,為所欲為。因?yàn)闉g覽器同時(shí)還規(guī)定蚕冬,提交表單不受同源政策的限制免猾。
由此可見,"同源政策"是必需的囤热,否則 Cookie 可以共享猎提,互聯(lián)網(wǎng)就毫無(wú)安全可言了。

再來(lái)一段Wikipedia上的描述:

In computing, the same-origin policy is an important concept in the web application security model. Under the policy, a web browser permits scripts contained in a first web page to access data in a second web page, but only if both web pages have the same origin. An origin is defined as a combination of URI scheme, hostname, and port number.This policy prevents a malicious script on one page from obtaining access to sensitive data on another web page through that page's Document Object Model.
This mechanism bears a particular significance for modern web applications that extensively depend on HTTP cookies to maintain authenticated user sessions, as servers act based on the HTTP cookie information to reveal sensitive information or take state-changing actions. A strict separation between content provided by unrelated sites must be maintained on the client-side to prevent the loss of data confidentiality or integrity.

看了這么多的描述之后旁蔼,我也來(lái)一個(gè)自己總結(jié)的吧锨苏。

我認(rèn)為同源策略之所以出現(xiàn),就是為了提高在瀏覽器上網(wǎng)的安全性棺聊。這里的 安全性主要是針對(duì) cookie來(lái)說(shuō)的伞租。因?yàn)?cookie 保存著我們每個(gè)人的上網(wǎng)信息,甚至包含一些涉及個(gè)人重要隱私的信息限佩,而沒(méi)有同源策略之前葵诈,我們的這些信息,也即 cookie很容易被他人利用祟同,去做一些羞羞的事情驯击,所以為了保證我們每個(gè)網(wǎng)民能夠安全的上網(wǎng),這個(gè)策略就應(yīng)用而生啦耐亏。(在這里,我要對(duì)NetScape說(shuō)一聲感謝)

** 那么沪斟,回歸正題广辰,這個(gè)同源策略的具體作用都有哪些呢? **

一旦我們進(jìn)行非同源的跨站訪問(wèn)主之,那么為了安全起見择吊,在這個(gè)過(guò)程中,有一些數(shù)據(jù)的傳輸會(huì)受到限制槽奕。比如:

  • Cookie几睛、LocalStorage 和 IndexDB 無(wú)法讀取。
  • DOM無(wú)法獲得
  • AJAX請(qǐng)求不能發(fā)送

這樣一來(lái)粤攒,雖然看起來(lái)我們的安全性被牢牢地掌握在自己手中所森,但是自己的手腳也多多少少受到了一些限制囱持,什么生意呢?如果在我們自己搞的東西中焕济,真的需要進(jìn)行跨域訪問(wèn)呢纷妆?難道當(dāng)初NetScape在設(shè)計(jì)的時(shí)候就沒(méi)考慮到這一點(diǎn)嗎?

放心晴弃,其實(shí)是有辦法的掩幢。

2.那么都有哪些辦法能夠繞過(guò)同源策略來(lái)實(shí)現(xiàn)相應(yīng)的功能呢?

  1. 對(duì)于Cookie的傳輸:
    1.1 如果我們現(xiàn)在有兩個(gè)網(wǎng)頁(yè)上鞠,它們一級(jí)域名一致际邻,只是二級(jí)域名不同,那么如果要做到使這兩個(gè)域名能夠?qū)崿F(xiàn)cookie的共享芍阎,只需要將這兩個(gè)頁(yè)面的 document.domain設(shè)置為一級(jí)域名即可世曾。
    比如 頁(yè)面一 http://xiao.haha.com/a.html 頁(yè)面二 http://qian.haha.com/b.html 那么只需要將 document.domain = haha.com即可
    1.2 對(duì)于兩個(gè)毫無(wú)關(guān)系的頁(yè)面,如果要進(jìn)行cookie的傳輸能曾,那么需要借助標(biāo)簽 <img> <script> <iframe> 即可(這里就是CSRF攻擊的起源之地)

  2. 對(duì)于DOM的傳輸:
    2.1 片段識(shí)別符(fragment identifier)
    2.2 window.name
    2.3 跨文檔通信API(Cross-document messaging)

3.對(duì)于AJAX請(qǐng)求:
3.1 利用JSONP通信標(biāo)準(zhǔn)
其基本思想是既然AJAX已經(jīng)被廢掉了度硝,那么就換一種還可以繼續(xù)請(qǐng)求的辦法。于是就想到了可以利用 <script> 標(biāo)簽來(lái)向跨源地址發(fā)起請(qǐng)求寿冕。
示例代碼

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://example.com/ip?callback=foo');
}
function foo(data) {
  console.log('Your public IP address is: ' + data.ip);
};

上面代碼通過(guò)動(dòng)態(tài)添加 <script> 元素蕊程,向服務(wù)器 example.com 發(fā)出請(qǐng)求。注意驼唱,該請(qǐng)求的查詢字符串有一個(gè) callback 參數(shù)藻茂,用來(lái)指定回調(diào)函數(shù)的名字,這對(duì)于JSONP是必需的玫恳。
服務(wù)器收到這個(gè)請(qǐng)求以后辨赐,會(huì)將數(shù)據(jù)放在回調(diào)函數(shù)的參數(shù)位置返回。

foo({
  "ip": "8.8.8.8"
});

由于 <script> 元素請(qǐng)求的腳本京办,直接作為代碼運(yùn)行掀序。這時(shí),只要瀏覽器定義了foo函數(shù)惭婿,該函數(shù)就會(huì)立即調(diào)用不恭。作為參數(shù)的JSON數(shù)據(jù)被視為JavaScript對(duì)象,而不是字符串财饥,因此避免了使用 JSON.parse 的步驟换吧。

3.2 利用WebSocket協(xié)議
WebSocket是一種通信協(xié)議,使用ws://(非加密)和wss://(加密)作為協(xié)議前綴钥星。該協(xié)議不實(shí)行同源政策沾瓦,只要服務(wù)器支持,就可以通過(guò)它進(jìn)行跨源通信。

示例如下:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Origin: http://example.com/

在上邊的示例中贯莺,最后一個(gè)屬性 origin 是重點(diǎn)风喇。該字段表示請(qǐng)求的請(qǐng)求源,然后瀏覽器根據(jù)該地址乖篷,判斷是否處在白名單中响驴,如果處在,則可以進(jìn)行本次通信撕蔼,反之不可豁鲤。

3.3 利用CORS(跨源資源分享)標(biāo)準(zhǔn)
CORS是一個(gè)W3C標(biāo)準(zhǔn),全稱是"跨域資源共享"(Cross-origin resource sharing)鲸沮。
它允許瀏覽器向跨源服務(wù)器琳骡,發(fā)出XMLHttpRequest請(qǐng)求,從而克服了AJAX只能同源使用的限制讼溺。

瀏覽器將CORS請(qǐng)求分成兩類:簡(jiǎn)單請(qǐng)求(simple request)和非簡(jiǎn)單請(qǐng)求(not-so-simple request)楣号。
只要同時(shí)滿足以下兩大條件,就屬于簡(jiǎn)單請(qǐng)求怒坯。

  • 請(qǐng)求方法是以下三種方法之一:
    1.HEAD
    2.GET
    3.POST
  • HTTP的頭信息不超出以下幾種字段:
    1.Accept
    2.Accept-Language
    3.Content-Language
    4.Last-Event-ID
    5.Content-Type:只限于三個(gè)值application/x-www-form-urlencoded炫狱、multipart/form-data、text/plain

** 對(duì)于簡(jiǎn)單請(qǐng)求: **
瀏覽器直接發(fā)出CORS請(qǐng)求剔猿。具體來(lái)說(shuō)视译,就是在頭信息之中,增加一個(gè)Origin字段归敬。
如果Origin指定的源酷含,不在許可范圍內(nèi),服務(wù)器會(huì)返回一個(gè)正常的HTTP回應(yīng)汪茧。瀏覽器發(fā)現(xiàn)椅亚,這個(gè)回應(yīng)的頭信息沒(méi)有包含Access-Control-Allow-Origin字段(詳見下文),就知道出錯(cuò)了舱污,從而拋出一個(gè)錯(cuò)誤呀舔,被XMLHttpRequest的onerror回調(diào)函數(shù)捕獲。注意扩灯,這種錯(cuò)誤無(wú)法通過(guò)狀態(tài)碼識(shí)別别威,因?yàn)镠TTP回應(yīng)的狀態(tài)碼有可能是200。

** 對(duì)于非簡(jiǎn)單請(qǐng)求: **
非簡(jiǎn)單請(qǐng)求是那種對(duì)服務(wù)器有特殊要求的請(qǐng)求驴剔,比如請(qǐng)求方法是PUT或DELETE,或者Content-Type字段的類型是application/json粥庄。
非簡(jiǎn)單請(qǐng)求的CORS請(qǐng)求丧失,會(huì)在正式通信之前,增加一次HTTP查詢請(qǐng)求惜互,稱為"預(yù)檢"請(qǐng)求(preflight)布讹。
瀏覽器先詢問(wèn)服務(wù)器琳拭,當(dāng)前網(wǎng)頁(yè)所在的域名是否在服務(wù)器的許可名單之中,以及可以使用哪些HTTP動(dòng)詞和頭信息字段描验。只有得到肯定答復(fù)白嘁,瀏覽器才會(huì)發(fā)出正式的XMLHttpRequest請(qǐng)求,否則就報(bào)錯(cuò)膘流。

** 注: CORS協(xié)議支持所有的HTTP請(qǐng)求絮缅,而JSONP只支持GET請(qǐng)求,所以在這一點(diǎn)上呼股,CORS協(xié)議要強(qiáng)大的多耕魄。 **

3.CSRF攻擊又是啥子武器呢?

3.1 什么是CSRF攻擊彭谁?

CSRF(Cross-site request forgery)吸奴,中文名稱:跨站請(qǐng)求偽造,也被稱為:one click attack/session riding缠局,縮寫為:CSRF/XSRF则奥。

3.2 CSRF攻擊的原理?

利用cookie狭园,盜用身份從而進(jìn)行邪惡活動(dòng)读处。

具體的作案手段都有哪些呢?

示例1:
銀行網(wǎng)站A妙啃,它以GET請(qǐng)求來(lái)完成銀行轉(zhuǎn)賬的操作档泽,如:http://www.mybank.com/Transfer.php?toBankId=11&money=1000
危險(xiǎn)網(wǎng)站B,它里面有一段HTML的代碼如下,便會(huì)成功揖赴。

<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

** 由于同源策略對(duì)img標(biāo)簽沒(méi)有限制馆匿,所以向在img標(biāo)簽中的src地址發(fā)起請(qǐng)求時(shí),會(huì)直接將用戶的cookie傳遞過(guò)去燥滑,從而盜取用戶身份進(jìn)行非法操作渐北。 **

示例2:
為了杜絕上面的問(wèn)題,銀行決定改用POST請(qǐng)求完成轉(zhuǎn)賬操作铭拧。
銀行網(wǎng)站A的WEB表單如下:

  <form action="Transfer.php" method="POST">
    <p>ToBankId: <input type="text" name="toBankId" /></p>
    <p>Money: <input type="text" name="money" /></p>
    <p><input type="submit" value="Transfer" /></p>
  </form>

后臺(tái)處理頁(yè)面Transfer.php如下:

<?php
    session_start();
    if (isset($_REQUEST['toBankId'] && isset($_REQUEST['money']))
    {
        buy_stocks($_REQUEST['toBankId'], $_REQUEST['money']);
    }
  ?>

危險(xiǎn)網(wǎng)站B赃蛛,仍然只是包含那句HTML代碼,同樣也會(huì)成功搀菩。

<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

** 為什么這次還可以呢呕臂,這個(gè)鍋就得PHP背了,PHP在后端用的是$_REQUEST來(lái)獲取參數(shù)肪跋,那么它既可獲取GET參數(shù)歧蒋,也可以得到POST。所以在這里,依然用的是GET請(qǐng)求方式了谜洽, 同樣地在JAVA中萝映, request對(duì)象也是如此,不能區(qū)分GET和POST**

示例3:
經(jīng)過(guò)前面2個(gè)慘痛的教訓(xùn)阐虚,銀行決定把獲取請(qǐng)求數(shù)據(jù)的方法也改了序臂,改用$_POST,只獲取POST請(qǐng)求的數(shù)據(jù)实束,后臺(tái)處理頁(yè)面Transfer.php代碼如下:

  <?php
    session_start();
    if (isset($_POST['toBankId'] && isset($_POST['money']))
    {
        buy_stocks($_POST['toBankId'], $_POST['money']);
    }
  ?>

然而奥秆,危險(xiǎn)網(wǎng)站B與時(shí)俱進(jìn),它改了一下代碼磕洪,照樣得手:

<html>
  <head>
    <script type="text/javascript">
      function steal()
      {
               iframe = document.frames["steal"];
               iframe.document.Submit("transfer");
      }
    </script>
  </head>

  <body onload="steal()">
    <iframe name="steal" display="none">
      <form method="POST" name="transfer" action="http://www.myBank.com/Transfer.php">
        <input type="hidden" name="toBankId" value="11">
        <input type="hidden" name="money" value="1000">
      </form>
    </iframe>
  </body>
</html>

其實(shí)在網(wǎng)站B的代碼中吭练,就是通過(guò)利用同源策略對(duì)iframe標(biāo)簽的不限制,從而在iframe標(biāo)簽中又重新構(gòu)造了一個(gè)post方式提交的表單析显,同樣奏效了鲫咽。

3.3 那么如何防御呢?

  1. 在服務(wù)器端進(jìn)行防御 :Cookie Hashing(所有表單都包含同一個(gè)偽隨機(jī)值):
    在表單里增加Hash值谷异,以認(rèn)證這確實(shí)是用戶發(fā)送的請(qǐng)求分尸。
 <?php
    $hash = md5($_COOKIE['cookie']);
  ?>
  <form method=”POST” action=”transfer.php”>
    <input type=”text” name=”toBankId”>
    <input type=”text” name=”money”>
    <input type=”hidden” name=”hash” value=”<?=$hash;?>”>
    <input type=”submit” name=”submit” value=”Submit”>
  </form>

然后在服務(wù)器端進(jìn)行Hash值驗(yàn)證

   <?php
        if(isset($_POST['check'])) {
             $hash = md5($_COOKIE['cookie']);
             if($_POST['check'] == $hash) {
                  doJob();
             } else {
        //...
             }
        } else {
      //...
        }
      ?>

利用此種hash方法,即使他人拿到cookie歹嘹,也無(wú)法再次獲取到原來(lái)的那個(gè)值箩绍,也就不能再盜取身份了。所以這種防御幾乎可以杜絕很大一部分的攻擊了尺上,但是還有一小部分的不能防御到材蛛,是因?yàn)橐坏┯龅絏SS攻擊,讓他人拿到原始的cookie數(shù)據(jù)怎抛,那同樣GG卑吭。

  1. 每次提交表單都輸入驗(yàn)證碼。此種方法太過(guò)繁瑣马绝,不符合實(shí)際使用情況豆赏。

  2. One-Time Tokens(不同的表單包含一個(gè)不同的偽隨機(jī)值)

4.小結(jié):

這篇文章是對(duì)同源策略及其安全性方面進(jìn)行的了解和小結(jié)。其中還有一些細(xì)節(jié)性的問(wèn)題沒(méi)有理解到位富稻,這次總結(jié)的目的并不會(huì)糾結(jié)于細(xì)節(jié)之處掷邦,而是從一個(gè)整體的角度來(lái)了解、學(xué)習(xí)一下這方面的東西椭赋,讓自己有一個(gè)大致的印象即可抚岗。

5.參考文章:

瀏覽器同源政策及其規(guī)避方法-阮一峰
跨域資源共享 CORS 詳解-阮一峰
淺談CSRF攻擊方式
同源策略和跨域請(qǐng)求研究

最後編輯於
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市哪怔,隨后出現(xiàn)的幾起案子宣蔚,更是在濱河造成了極大的恐慌廷痘,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件件已,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡元暴,警方通過(guò)查閱死者的電腦和手機(jī)篷扩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)茉盏,“玉大人鉴未,你說(shuō)我怎么就攤上這事○蹋” “怎么了铜秆?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)讶迁。 經(jīng)常有香客問(wèn)我连茧,道長(zhǎng),這世上最難降的妖魔是什么巍糯? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任啸驯,我火速辦了婚禮,結(jié)果婚禮上祟峦,老公的妹妹穿的比我還像新娘罚斗。我一直安慰自己,他們只是感情好宅楞,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布针姿。 她就那樣靜靜地躺著,像睡著了一般厌衙。 火紅的嫁衣襯著肌膚如雪距淫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天迅箩,我揣著相機(jī)與錄音溉愁,去河邊找鬼。 笑死饲趋,一個(gè)胖子當(dāng)著我的面吹牛拐揭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播奕塑,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼堂污,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了龄砰?” 一聲冷哼從身側(cè)響起盟猖,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤讨衣,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后式镐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體反镇,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年娘汞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了歹茶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡你弦,死狀恐怖惊豺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情禽作,我是刑警寧澤尸昧,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站旷偿,受9級(jí)特大地震影響烹俗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狸捅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一衷蜓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尘喝,春花似錦磁浇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至缔赠,卻和暖如春衍锚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嗤堰。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工戴质, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人踢匣。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓告匠,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親离唬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子后专,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容