CORS漏洞的學習與分析
- 2020 年 4 月 18 日
- 筆記
同源策略
同源策略(Same origin policy)是一種約定,一種非常重要的安全措施,也是最基本的安全功能,它禁止了來自不同源的腳本對當前頁面的讀取或修改,從而限制了跨域訪問甚至修改資源,防止出現A頁面可以隨意更改B頁面信息這樣子的極其糟糕的情況發生。
同源策略做了怎樣的限制呢?怎樣才會被認為是跨域的,不同源的呢?
以//www.a.com為例:
正常情況 | //www.a.com | 允許 |
不同域名 | //www.b.com | 不允許 |
不同協議 | //www.a.com | 不允許 |
不同端口 | //www.a.com:10 | 不允許 |
不同子域名 | //bp.a.com | 不允許 |
值得注意是:同源策略是在瀏覽器實現的所以它無法阻止請求和響應的發送,當跨域請求發起後,實際上已經接收到了所請求的資源,如果我們攔截來響應包,就可以發現,所請求的資源已經包含在響應的包中了,瀏覽器發現跨域,根據同源策略,瀏覽器不讓所請求的資源加載出來。
hack.com將從test.com上請求數據
發現瀏覽器遵守同源策略阻止了跨域讀取的資源的加載,那麼這些想讀取的資源究竟是根本就沒有發送還是已經發送了卻被瀏覽器給禁止加載了呢?
可以很明顯的看出,已經接收了所請求的資源,但因為不同源被瀏覽器攔截了。
CORS是什麼?
隨着時代的發展,技術的進步,新的需求逐漸產生。人們開始有了跨域請求資源的需要,同源策略這樣一股腦的禁止所有的跨域請求顯然不合適,但放棄同源策略更是不可能的,允許不同源的頁面相互作用是一場災難。在這樣的需求與矛盾下,CORS應運而生。
跨域資源共享(Cross-origin resource sharing),簡稱CORS,它使某些頁面可以跨域請求來自特定的不同源的頁面資源。
CORS依託於瀏覽器,如果瀏覽器不支持CORS,那麼便無法使用,不過現在幾乎沒有不支持CORS的瀏覽器。
當發出跨域請求時,瀏覽器會在請求報文頭中加入字段 Origin:xxx 來說明發起跨域請求的源,當另一端的服務器接受到了帶有Origin的請求時,它會將所請求的資源放入響應報文中返回,並在響應頭中加入一些以 Access-Control- 開頭的字段,我們需要關注的只有其中兩個:
- Access-Control-Allow-Origin: 它的值只能是一個外域的URI或者是*(只能有一個URI或*,不能寫像 Access-Control-Allow-Origin://a.com,//b.com 這樣的,雖然這樣很方便,但它是不被允許的,該字段後只允許有唯一一個URI,下文會說一種設置多個允許跨站的域的方法),這個值是允許進行跨域請求的源,瀏覽器接收響應後先比對這個字段中的URI是不是發起請求的Origin,若為*則允許所有域名的請求。如果兩者一樣,則允許這次跨域請求,響應中的所請求的資源將會被加載到瀏覽器上。
- Access-Control-Allow-Credentials: 這個字段的值只能是true,則接收的數據中包含cookie,若不設置該字段,則不發送cookie。
如此,通過CORS,可以實現幾個特定的域之間的跨域的訪問。
CORS設置:
修改一下hosts,模擬兩個不同的站點:
192.168.10.103 www.test.com 192.168.10.103 www.hack.com
按照自己的情況修改ip,
如果跨站請求成功會顯示success,
被請求的url,//www.test.com/test/test.php
<?php
echo 'success'; ?>
發起跨域請求的url,//www.hack.com/test/1.html
<!DOCTYPE> <html> <p>Hello<p> <script type="text/javascript"> function test() { var ajax = new XMLHttpRequest(); ajax.onreadystatechange=function() { if(ajax.readyState == 4 && ajax.status == 200) { var text = ajax.responseText; document.write(text) } } ajax.open("GET","//www.test.com/test/test.php","true") ajax.send(); } test(); </script> </html>
在hack.com的頁面上請求test.com的資源:
顯示禁止跨域發出xmlhttprequest請求
<?php header('Access-Control-Allow-Origin://www.hack.com'); echo 'success'; ?>
加入Access-Control-Allow-Origin字段允許hack.com的源跨站請求資源。
跨站訪問成功。
當我們把Access-Control-Allow-Origin改為//www.a.com時:
又失敗了。
漏洞的產生
漏洞產生的原因可分為兩個,它們的原因都出在Access-Control-Allow-Origin上,當配置為星號*時,來自任何一個域名上的腳本都可以請求這個頁面上的所有信息,相當於手動禁止了同源策略,這個原因的可能性極小,因為除非是一個全無經驗的人,否則沒有人會將其設置為*。另一個原因出現的幾率就遠遠大於第一個了,其原因為由程序員的大意或疏忽導致匹配的不嚴謹。
不完全匹配:
這種問題是由於當按一定的規則來確認發起跨域請求的源是否應該允許訪問時,匹配的規則有紕漏,只確認了其中某一部分符合規則就認為源一定是可信任的,例如:
將//www.test.com/test/test.php進行更改
<?php $origin = $_SERVER['HTTP_ORIGIN']; $pattern = '#www.a.com#'; preg_match($pattern,$origin,$match); if ($match) { header('Access-Control-Allow-Origin:'.$origin); } echo 'success'; ?>
這段代碼中,只要匹配到www.hack.com,並沒有確認其他部分的內容就簡單的判斷允許跨域請求,當攻擊者使用 www.a.com.hack.com 時,很容易就騙過了規則,成功發起跨域請求並接收。
再舉個例子,當匹配規則為確認 a.com 在Origin末尾時才允許訪問,顯然上面一個例子中的方法不再適用,這時仍可以構造 bbbbbbba.com 這樣的域名來突破限制。
道高一尺魔高一丈,匹配規則有很多,突破方法有更多,這裡就不一一列舉了。
沒有轉義:
當用正則表達式進行匹配時,URI中存在着許多點 . 像 bp.a.com , tieba.baidu.com , www.billbill.com 小數點.這個符號在正則表達式可以代替着任意一個字符,如果想讓其僅僅代表.這個符號本身而不是匹配任何一個字符的話,應當使用轉義符\. ,但很多人往往總會忘記轉義,如在上述的代碼中就沒有進行轉義:
//www.test.com/test/test.php中注意:
$pattern = '#www.a.com#';
當我們使用 www.aacom.com , www.wwwaaacom.com , www.wwwaa.com 在 . 的位置隨意寫上一個字符就可以突破限制,正確寫法應該是www\.a\.com
當頭設置了Access-Control-Allow-Credentials:true時,恰巧產生了CORS漏洞,危害會大大增加,因為向其發起跨域請求時,它會將cookie一起發送,這是及其不希望看到的,也是非常危險的,讀取了頁面上的一些信息有時或許無傷大雅,但讀取了cookie很多時候都是非常恐怖的。
漏洞檢測與利用
CORS漏洞會加強xss的攻擊力,哪怕是反射型的xss也增加了許多可能性。
檢測CORS的方式可以通過不斷修改Origin,檢測響應中的Access-Control-Allow-Origin的值是否與Origin相同,如若相同,則存在CORS漏洞,github上有一個CORS漏洞的檢測工具CORScanner,當檢測到CORS時,可以這樣利用:
<script> var ajax = new XMLHttpRequest(); ajax.open('GET','//xxx','true'); ajax.onload = main; ajax.withCredentials = true; ajax.send() function main(){ xxx }
其中,ajax.withCredentials = true;這一段代碼的作用是將Access-Control-Allow-Credentials:true設置為true以允許讀取cookie,提升危害,main中寫入可以讀取敏感數據birucookie並將其發送給攻擊者的代碼,誘使受害者執行這段代碼,就可以達到獲取重要信息的目的。
——————————————————————————————————————————————————————————————————————————————————————
總體來說,CORS漏洞現在看來危害並不是特別大,但是千里之堤潰於蟻穴,無論怎樣的漏洞都要有足夠的重視,既是對自己負責,也是對他人負責、