cookie跨域_跨域问题的复现与整理
知乎:Sp4rkW
GITHUB:Sp4rkW
B站:一只技術(shù)君
博客:https://sp4rkw.blog.csdn.net/
聯(lián)系郵箱:getf_own@163.com
文章目錄
- 一、同源策略
- 1、同源策略的定義
- 2、什么是同源
- 3、同源策略在保護(hù)什么
- 二、環(huán)境搭建
- 1、服務(wù)器配置
- 2、寶塔站點安裝配置
- 三、同源策略限制js演示
- 1、基于iframe引入頁面的dom操作
- 1.1、相同ip相同端口
- 1.2、不同ip
- 1.3、相同ip不同端口
- 2、ajax跨域操作
- 2.1、相同ip
- 2.2、不同ip
- 1、基于iframe引入頁面的dom操作
- 四、實際挖掘漏洞中可能遇到的跨域漏洞以及防御措施
- 1、跨域?qū)懧┒?/li>
- 2、基于json格式的跨域?qū)懛烙?/li>
- 3、Access-Control-Allow-Origin:*是否存在漏洞
- 4、跨域讀漏洞
- 5、跨域問題的防御
- 五、文章參考來源
前兩周在90sec論壇上看到一篇挺有意思的文章,主要講的是同源策略的問題,作者寫的較為詳細(xì),其中有些內(nèi)容也解決了我之前的一些疑問。但是,看百次不如自己動手去做一次,此外,b站也好久沒有更新技術(shù)視頻了。于是我決定去復(fù)現(xiàn)一下這篇文章的所有實驗,并結(jié)合自己的一些經(jīng)驗,來聊一聊可能存在的一些漏洞該怎么挖掘與防御。視頻的完整內(nèi)容已經(jīng)上傳b站,各位看官可以移步觀看,有賬號的同學(xué)能給個一鍵三連就再好不過了~
https://www.bilibili.com/video/BV1Ha4y1a7h8/?www.bilibili.com漏洞挖掘中的同源策略 P2_嗶哩嗶哩 (゜-゜)つロ 干杯~-bilibili?www.bilibili.comPS:第三期視頻因為我誤刪了環(huán)境虛擬機(jī),懶得配置了,就沒有做了,直接看文章吧~一、同源策略
1、同源策略的定義
? 同源策略限制從一個源加載的文檔或腳本與另一個源的資源進(jìn)行交互,是用于隔離潛在惡意文件的重要機(jī)制。不同源的客戶端腳本在沒有明確授權(quán)的情況下,不能讀取對方資源。比如,瀏覽器執(zhí)行javascript腳本時,會檢查這個腳本屬于哪個頁面,如果不是同源頁面,就不會被執(zhí)行。
2、什么是同源
同源要求三個都相同:
- 端口
- IP
- 協(xié)議
3、同源策略在保護(hù)什么
同源策略對于js的限制有3點:
(1)無法用js讀取非同源的Cookie、LocalStorage 和 IndexDB 無法讀取
? 為了防止惡意網(wǎng)站通過js獲取用戶其他網(wǎng)站的cookie。
(2) 無法用js獲取非同源的DOM
? 如果沒有這一條,惡意網(wǎng)站可以通過iframe打開銀行頁面,可以獲取dom就相當(dāng)于可以獲取整個銀行頁面的信息。當(dāng)然,iframe標(biāo)簽允許加載,可能會造成另外一個問題,點擊劫持。點擊劫持這里我就不解釋了,詳情見我之前的視頻
https://www.bilibili.com/video/BV1i4411C7WP/(3) AJAX 請求限制發(fā)送
? 為什么要做這個請求限制呢?比如說我搭建了一個站點,我在我的頁面中寫入js請求,去請求百度修改賬號頭像。如果沒有跨域的保護(hù),我可以發(fā)送這個請求,并且你如果百度賬號是登錄狀態(tài),那你的頭像就被我修改了。
二、環(huán)境搭建
1、服務(wù)器配置
更加詳細(xì)的配置如圖所示:
2、寶塔站點安裝配置
之后直接給兩個服務(wù)器安裝寶塔,方便快速搭建php站點
wget -O install.sh http://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh安裝完成之后選擇一套集成環(huán)境安裝一下即可,我的話就選擇了nginx這一套,ftp不需要,所以就沒有安裝了,傳輸文件可以使用winscp來解決
之后創(chuàng)建個站點
為了方便對比,我總共創(chuàng)建了三個web站點,分別是:
至此環(huán)境配置完成
三、同源策略限制js演示
原文作者總共給出了三個實驗,分別是:
- 基于iframe標(biāo)簽引入跨域站點頁面的dom獲取cookie
- 測試ajax在不同同源策略下的表現(xiàn)
- 基于iframe標(biāo)簽引入的頁面的dom修改
由于第一個和第三個實驗本質(zhì)上是相同的,所以我合并起來進(jìn)行演示
1、基于iframe引入頁面的dom操作
構(gòu)造兩個頁面,用于測試,首先我們來看引入頁面的代碼:
<!-- js-iframe-115.html --><!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>js-iframe-115</title> </head> <body><iframe id="myframe" src="http://192.168.1.115/js-iframe-115.php"> </iframe><h2 id="A">使用iframe標(biāo)簽測試跨域訪問非同源網(wǎng)站獲取cookie測試</h2><input type="button" onclick="changeStyle_1()" value="獲取嵌入網(wǎng)站的cookie"><script>document.cookie = "ceshi6";function changeStyle_1() {var x = document.getElementById("myframe");var y = (x.contentWindow || x.contentDocument);if (y.document) y = y.document;alert(y.cookie);}</script><h2 id="A">使用iframe標(biāo)簽測試跨域修改背景色</h2><input type="button" onclick="changeStyle_2()" value="修改背景顏色"><script>function changeStyle_2(){var x = document.getElementById("myframe");var y = (x.contentWindow || x.contentDocument);if (y.document) y = y.document;y.body.style.backgroundColor = "#008800";}</script> </body> </html>上面的兩個部分,都是用iframe標(biāo)簽引入了http://192.168.1.115/js-iframe-115.php頁面,作用分別是,提取這個站點的cookie與修改頁面背景色。再來看下被引入頁面的代碼:
<!-- js-iframe-115.php --><!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>js-iframe-115-php</title><style></style> </head> <body><div class="container"><p>用于測試跨域讀取cookie</p></div> </body> </html> <?phpsetcookie('name','king',time()+3600);setcookie('mail','muke@imooc.com',time()+3600);setcookie('addr','beijing',time()+3600); ?>這里的代碼比較簡單,僅僅是設(shè)置cookie。接下來我們進(jìn)行三組對照實驗,我們將php文件放在115站點的80端口web服務(wù)下,之后在三個web服務(wù)中都放入html文件,這樣我們就可以分別從同ip同端口,同ip不同端口,不同ip來進(jìn)行測試。
1.1、相同ip相同端口
演示的動圖如下:
可以看到在同源的情況下,我們可以很容易的通過js獲取iframe標(biāo)簽引入的站點的dom,進(jìn)而獲取到cookie與修改頁面背景色
1.2、不同ip
演示的動圖如下:
可以看到在不同ip也就是不同源的情況下,js控制臺會產(chǎn)生警告,當(dāng)我們執(zhí)行js事件企圖獲取到引入站點的dom的時候,控制臺直接報錯
1.3、相同ip不同端口
演示的動圖如下:
對比不同ip的情況,我們可以發(fā)現(xiàn),在不同端口的情況下,控制臺沒有告警,但是執(zhí)行js的時候,依舊會報錯,無法執(zhí)行
這時候,問題來了,原文作者在這里實驗出了一個非常有趣的結(jié)果
? 就這個結(jié)果,我特地請教了原文作者,這里也非常感謝原文作者愿意和我溝通交流。最終我們發(fā)現(xiàn),第一,這個圖有問題,alert的是document.cookie,也就是當(dāng)前站點的地址,所以會彈窗;第二,為什么會彈出設(shè)置在另一個端口對應(yīng)web服務(wù)的cookie呢?這里就不得不提一個非常有意思的機(jī)制了
根據(jù)同源策略,cookie是區(qū)分端口的,但是瀏覽器實現(xiàn)來說,“cookie區(qū)分域,而不區(qū)分端口,也就是說,同一個ip下的多個端口下的cookie是共享的!“演示動圖如下:
? 進(jìn)一步搜索這個問題,我發(fā)現(xiàn)很多開發(fā)都在吐槽這個點,本地測試的時候非常惡心,cookie混亂。所以大部分開發(fā)在測試過程中都會給不同端口,同一ip的web服務(wù)分別分配不同的子域名,如http://test1.x.com,http://test2.x.com。這也就是為什么不同子域名明明對應(yīng)ip是一個,但是還會被瀏覽器跨域策略所限制。
? 這里其實引起了我的另一個思考,如果能找到一個站點的xss,同時獲取了服務(wù)器的真實ip,那么我可以通過這個xss,拿到同ip不同端口的其他服務(wù)的cookie,這個思路有待之后實戰(zhàn)測試一下。不過可能最大的限制在于,用戶應(yīng)該都是通過域名進(jìn)行訪問操作的,不過在特定站點都還有一些是通過ip直接訪問
2、ajax跨域操作
首先構(gòu)建兩個用于測試的頁面,我們先來看請求端的頁面:
<!-- js-ajax-115.html --><!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>js-ajax-115</title> </head> <body><p>請求失敗<p><script>var url = "http://192.168.1.115/js-ajax-115.php";function createXMLHttpRequest() {if (window.XMLHttpRequest) {XMLHttpR = new XMLHttpRequest();} else if (window.ActiveXObject) {try {XMLHttpR = new ActiveXObject("Msxml2.XMLHTTP");} catch (e) {try {XMLHttpR = new ActiveXObject("Microsoft.XMLHTTP");} catch (e) {}}}}function sendRequest(url) {createXMLHttpRequest();XMLHttpR.open("GET", url, true);XMLHttpR.setRequestHeader("Content-Type", "text/html;charset=utf-8");XMLHttpR.onreadystatechange = processResponse; XMLHttpR.send(null);}function processResponse() {if (XMLHttpR.readyState == 4 && XMLHttpR.status == 200) {document.write(XMLHttpR.responseText);}}sendRequest(url);</script> </body> </html>這個頁面總共有三個樣式,我們結(jié)合被請求頁面代碼的來看
<!-- js-ajax-115.php --><!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>js-ajax-115-php</title><style></style> </head><body><div class="container"><?phpif ($_COOKIE['user'] == 'test'){echo "<p>cookies滿足,跨域請求頁面測試</p>";}else{echo "<p>cookies不滿足,跨域請求頁面測試</p>";}?></div> </body> </html>? 在請求頁面被加載的時候,如果js里面的ajax請求加載失敗,會顯示請求失;js執(zhí)行成功卻沒攜帶cookie,則返回cookies不滿足;js執(zhí)行成功且攜帶cookie,返回cookies滿足。下面我們來進(jìn)行測試,將php文件放入115的默認(rèn)80web服務(wù)下,html分別放入113,115默認(rèn)的80web服務(wù)下,并手動給115站點賦予cookie鍵值對user-test。
2.1、相同ip
演示動圖如下:
可以看到,在同源的情況下,ajax的請求發(fā)送成功,且請求時正常攜帶了cookie
2.2、不同ip
演示動圖如下:
? 可以看到這里的network對115服務(wù)請求有一個報錯Provisional headers are shown,這個報錯大部分都是因為跨域問題所引起的,這個報錯代表瀏覽器阻止了ajax請求的發(fā)送。原文作者在這里提出了一個觀點,說請求成功發(fā)送了,但是接收方拒絕接受了
? 換成我這個實驗,意思就是說113服務(wù)器的ajax成功執(zhí)行并帶回來數(shù)據(jù),但是115并沒有接受。這和我的實驗結(jié)果再一次有所出入,于是我也用burp進(jìn)行了測試。
? 可以看到,我捕獲到了兩個流量,第一個是113的正常請求,第二個就是115的請求。但,需要注意的是,這里對115的請求并不是GET/POST,而是OPTIONS。那,什么是OPTIONS請求呢?
在 CORS 中,可以使用 OPTIONS 方法發(fā)起一個預(yù)檢請求,以檢測實際請求是否可以被服務(wù)器所接受。預(yù)檢請求報文中的 Access-Control-Request-Method 首部字段告知服務(wù)器實際請求所使用的 HTTP 方法;Access-Control-Request-Headers 首部字段告知服務(wù)器實際請求所攜帶的自定義首部字段。服務(wù)器基于從預(yù)檢請求獲得的信息來判斷,是否接受接下來的實際請求。? 注意,OPTIONS請求是瀏覽器給我們強(qiáng)行發(fā)送的,當(dāng)瀏覽器通過服務(wù)器的返回包發(fā)現(xiàn),服務(wù)器不接受你的跨域請求,于是接下來的實際跨域請求直接就不會發(fā)送了。
四、實際挖掘漏洞中可能遇到的跨域漏洞以及防御措施
? 原作者的第四章是關(guān)于跨域策略在開發(fā)中的規(guī)避措施,規(guī)避措施實際上指的就是這里我結(jié)合我自己的挖洞經(jīng)驗,來演示一下一些漏洞的產(chǎn)生原因和修復(fù)手段。修復(fù)手段可能遠(yuǎn)不止這些,但是我在實際漏洞挖掘中,碰到這些情況可能我一般就直接放棄了跨域漏洞的挖掘。
1、跨域?qū)懧┒?/h2>
提前說明一下,不做csrf漏洞基本原理的詳細(xì)演示了,如果下面的簡單演示沒有看懂的話,建議可以先去了解一下csrf再繼續(xù)看后續(xù)內(nèi)容。
首先我沒來構(gòu)建一個簡單演示環(huán)境,后端服務(wù)器代買如下:
<!-- csrf-115.php --><!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>csrf-115-php</title><style></style> </head> <body><div class="container"><?phpheader('content-type:text/html;charset=utf8');header('Access-Control-Allow-Origin:192.168.1.111');header('Access-Control-Allow-Methods:POST');header('Access-Control-Allow-Headers:x-requested-with,content-type,Accept');$myfile = fopen("demo.txt", "w");if ($_COOKIE['user'] == 'test'){$txt = "cookies滿足 ".$_POST['data'];fwrite($myfile, mb_convert_encoding( $txt, 'UTF-8', mb_detect_encoding($txt)));}else{$txt = "cookies不滿足 ".$_POST['data'];fwrite($myfile, mb_convert_encoding( $txt, 'UTF-8', mb_detect_encoding($txt)));}fclose($myfile);?></div> </body> </html>這個后端對應(yīng)的前端頁面我直接忽略沒寫了,按正常邏輯,前端有一個表單,示意圖如下:
? 這一塊設(shè)計的正常邏輯是,前端表單填寫數(shù)據(jù),后端php處理post傳輸過來的data數(shù)據(jù),如果有cookie,則寫入本地,并備注cookie滿足;如果沒有cookie,也寫入本地文件,只不過會標(biāo)注cookie不滿足。按照漏洞挖掘的思路來說,這里我們首先測試的就是csrf漏洞,使用burp輔助構(gòu)造poc頁面如下:
<!-- csrfpoc.html --><html> <body><script>history.pushState('', '', '/')</script><form action="http://192.168.1.115/csrf-115.php" method="POST"><input type="hidden" name="data" value="123456" /><input type="submit" value="Submit request" /></form> </body> </html>? 將php文件放在115服務(wù)器上,將html文件放在113服務(wù)器上(這里我直接忽略同ip的情況了,這就是正常的前后端頁面,肯定可以執(zhí)行),按照第三步得出的結(jié)論,我們會受到瀏覽器同源策略的限制。115的地址還是一樣,我已經(jīng)手動給了user-test鍵值對的cookie。演示動圖如下:
再來看下我們服務(wù)器的本地文件:
可以看到,我成功構(gòu)造了一次csrf攻擊,同時可以看到同源策略限制的192.168.1.111完全沒有任何作用,origin就是192.168.1.115。但完全沒有受到影響,因為同源策略限制的是js,不限制表單。那這里應(yīng)該怎么防御呢?三種較為簡單的思路:
- 在表單中增加一個csrf-token參數(shù)
- 服務(wù)端校驗referer來源
- 將前端數(shù)據(jù)由表單發(fā)送修改為使用ajax發(fā)送的json類型數(shù)據(jù),后端強(qiáng)校驗json類型
前兩個防御措施很容易理解,我們來具體聊一聊第三個防御措施。這個防御測試基于兩個原理:第一,單純依靠html表單無法發(fā)送json類型數(shù)據(jù);第二,js會被同源策略所限制。下面我們來看更具體的例子
2、基于json格式的跨域?qū)懛烙?/h2>
首先,我們基于上一個實驗,修改后端處理代碼為接受json格式數(shù)據(jù),并強(qiáng)檢驗Content-Type頭需要設(shè)置為application/json,代碼如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>csrf-strict-json</title><style></style> </head> <body><div class="container"><?phpheader('Access-Control-Allow-Origin:http://192.168.1.113');header('Access-Control-Allow-Credentials:true');header('Access-Control-Allow-Headers:content-type');header('Access-Control-Allow-Methods:*');if($_SERVER["CONTENT_TYPE"] == "application/json"){$post_array=$GLOBALS['HTTP_RAW_POST_DATA'];//--解析Json,獲取對應(yīng)的變量值$obj=json_decode($post_array,TRUE);$data = $obj['data'];echo $data;$myfile = fopen("demo.txt", "w");if ($_COOKIE['user'] == 'test'){$txt = "cookies滿足 ".$data;fwrite($myfile, mb_convert_encoding( $txt, 'UTF-8', mb_detect_encoding($txt)));}else{$txt = "cookies不滿足 ".$data;fwrite($myfile, mb_convert_encoding( $txt, 'UTF-8', mb_detect_encoding($txt)));}fclose($myfile);}else{echo "不接收該類型傳參";} ?></div> </body> </html>因為做了強(qiáng)校驗,所以使用burp直接構(gòu)造的poc已經(jīng)沒有辦法利用了。關(guān)于為什么無法利用,瀏覽器在很老的版本之前就已經(jīng)開始禁用html表單發(fā)送json數(shù)據(jù)了,這里不做過多說明。那此時是不是安全了呢?并不是,我們可以通過js來發(fā)送json格式數(shù)據(jù),構(gòu)造頁面如下:
<html><head><script style="text/javascript">function submitRequest(){var xhr = new XMLHttpRequest();xhr.open("POST", "http://192.168.1.115/csrf-strict-json.php", true);xhr.setRequestHeader("Accept", "application/json, text/plain, */*");xhr.setRequestHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");xhr.setRequestHeader("Content-Type", "application/json");xhr.withCredentials = true;xhr.send(JSON.stringify({"data": "json12300"}));}</script></head><body><form action="#"><input type="button" value="Submit request" onClick="submitRequest()"/></form></body></html>演示動圖如下:
服務(wù)器的本地文件如下:
上圖證明,可以利用js跨域再一次證明存在csrf漏洞,但我們需要注意一點,這里我將115的web服務(wù)跨域保護(hù)對113服務(wù)器解除了,并且允許跨域請求且攜帶cookie。
header('Access-Control-Allow-Origin:http://192.168.1.113'); header('Access-Control-Allow-Credentials:true'); header('Access-Control-Allow-Headers:content-type'); header('Access-Control-Allow-Methods:*');這四行的代碼的意思分別是:允許來自113的跨域請求,允許跨域請求攜帶cookie,允許跨域option預(yù)檢驗,接受一切請求類型數(shù)據(jù)包
相關(guān)的演示過程有點多,不繼續(xù)截動圖了,有興趣的請自行驗證這幾行代碼的作用
3、Access-Control-Allow-Origin:*是否存在漏洞
簡單的修改一下上一個實驗的代碼為
header('Access-Control-Allow-Origin:*');之后,我們重復(fù)上一個實驗,演示動圖如下:
可以看到我們這次被跨域保護(hù)給攔截了,用burp抓包看一下
可以看到,是允許所有域進(jìn)行跨域的*,那為什么這里的option預(yù)檢驗確失敗了呢?
https://fetch.spec.whatwg.org/#resource-requests翻譯一下,簡單的意思就是說,如果是*,則不允許攜帶cookie,我們再來修改下poc頁面的代碼
// xhr.withCredentials = true;加上注釋,表明不攜帶cookie,演示動圖如下:
服務(wù)器的本地文件如下:
可以看到,這次成功跨域了,但是cookie沒有攜帶。綜上,我們可以認(rèn)為,跨域策略為*的時候其實并沒有實際危害性,因為跨域請求不可以攜帶cookie,這基本上不會執(zhí)行任何敏感操作。
4、跨域讀漏洞
跨域讀主要有兩種表現(xiàn):
- cors保護(hù)機(jī)制存在問題,僅僅通過csrftoken保護(hù)了存在寫漏洞的地方
- jsonp跨域讀取
cors讀的實驗不演示了,參考之前的通過iframe引入頁面的dom操作,這就是漏洞其中的一種表現(xiàn)。jsonp跨域的話,我前年的一篇博客已經(jīng)做了較為詳細(xì)的演示了,詳見:
CSDN-專業(yè)IT技術(shù)社區(qū)-登錄?sp4rkw.blog.csdn.net5、跨域問題的防御
結(jié)合我平時的經(jīng)驗,一般的防御措施以及可能的問題如下:
1、CORS跨域機(jī)制完善+所有接口強(qiáng)制POST Json格式
這個說起來很簡單,做起來其實挺難的,隨著業(yè)務(wù)的拓展,很多接口開發(fā)一不注意,為了方便就直接改了CORS的保護(hù)機(jī)制,而且一般CORS的保護(hù)機(jī)制是單獨一個模塊,引用對整個站點生效的。所以僅僅依靠CORS機(jī)制,很容易出問題。
2、CSRF-Token保護(hù)機(jī)制
這個對付跨域?qū)懧┒春芎糜?#xff0c;但是跨域讀,很容易忽略保護(hù)
3、Referer檢測機(jī)制
這個我個人認(rèn)為是最簡單,但應(yīng)用起來最方便的一個保護(hù)機(jī)制了。但是如果存在比如xss漏洞,url重定向問題,甚至referer檢驗不嚴(yán)格等等,這個機(jī)制也就完全失效了
4、自定義header頭
跨域的時候header頭是不可以控制的,當(dāng)然,如果服務(wù)器的cors策略接收這個header,還是可以繼續(xù)跨域操作的
不同機(jī)制適用于不同場景,結(jié)合起來可能會有更好的效果。
文章到此結(jié)束,暫時沒想到還有什么沒有寫的了,如果你有任何問題,歡迎與我進(jìn)行交流~
五、文章參考來源
瀏覽器同源策略是什么?沒有同源策略會怎么樣?
對同源策略的進(jìn)一步研究
跨域請求如何實現(xiàn)自定義 header?
再遇CORS – 自定義HTTP header的導(dǎo)致跨域
JSONP跨域和CORS跨域
Access-control-allow-origin:*并沒有實際危害
總結(jié)
以上是生活随笔為你收集整理的cookie跨域_跨域问题的复现与整理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 异常Exception
- 下一篇: HDU1247Hat’s Words(字