阿赫亞web安全JSON

eddie小英俊發表於2017-11-23

前言

JSON(JavaScript Object Notation),可以說,這一事實,瀏覽器,server資料交換標準。的格式如XML,或者其他自己定義的格式會越來越少。



為什麼JSON這麼流行?

和JavaScript無縫對接是一個原因。



另一個重要原因是能夠比較輕鬆的實現跨域。假設是XML。或者其他專有格式,則非常難實現跨域。要通過flash之類來實現。



不論什麼一種資料格式,怎樣解析處理不當。都會存在安全漏洞。

以下扯談下JSON相關的一些安全東東。

在介紹之前,先來提幾個問題:

  • 為什麼XMLHttpRequest要遵守同源策略?
  • XMLHttpRequest 請求會不會帶cookie?
  • <script scr=”…”> 的標籤請求會不會帶cookie?
  • 向一個其他域名的站點提交一個form,會不會帶cookie?
  • CORS請求能不能帶cookie?

JSON注入

有的時候,可能是為了方便。有人會手動拼接下JSON,可是這樣的隨手程式碼,卻可能帶來意想不到的安全隱患。
第一種方式。利用字串拼接:

		String user = "test01";
		String password = "12345`, admin:`true";
		String json = "{user:`%s`, password:`%s`}";
		System.out.println(String.format(json, user, password));
		//{user:`test01`, password:`12345`, admin:`true`}


使用者新增了管理員許可權。
另外一種,利用Parameter pollution, 類似http parameter pollution

		String string = "{user:`test01`,password:`hello`, password:`world`}";
		JSONObject parse = JSON.parseObject(string);
		String password = parse.getString("password");
		System.out.println(password);
		//world

當JSON資料key反覆了會怎麼處理?大部分JSON解析庫都是後面的引數覆蓋了前面的。


以下的演示了改動別人password的樣例:

		//user%3Dtest01%26password%3D12345%27%2Cuser%3Dtest02"
		//user=test01&password=12345`,user=test02
		HttpServletRequest request = null;
		String user = request.getParameter("user");
		//檢查test01是否登陸
		String password = request.getParameter("password");
		String content = "{user:`" + user + "`, password:`" + password + "`}";
		 
		User user = JSON.parseObject(content, User.class);
		//{"password":"12345","user":"test02"}
		updateDb(user);


所以說。不要手動拼接JSON字串

瀏覽器端應該怎樣處理JSON資料?

像eval這樣的方式,自然是不能採用的。

如今的瀏覽器都提供了原生的方法 JSON.parse(str) 來轉換為JS物件。

假設是IE8之前的瀏覽器,要使用這個庫來解析:https://github.com/douglascrockford/JSON-js

參考:http://zh.wikipedia.org/wiki/JSON#.E5.AE.89.E5.85.A8.E6.80.A7.E5.95.8F.E9.A1.8C

JQuery裡內建了JSON解析庫


JSONP callback注入

簡介jsonp的工作原理。

jsonp工作原理:

用js在document上插入一個<script>標籤。標籤的src指向遠端server的API地址。client和server約定一個回撥函式的名字,然後server返回回撥函式包裹著的資料,然後瀏覽器執行回撥函式,取得資料。
比方jquery是這樣子實現的:

var url = `http://localhost:8080/testJsonp?callback=?

`;
$.getJSON(url, function(data){
alert(data)
});

jquery自己主動把?

轉成了一個帶時間戳特別的函式(防止快取):

  http://localhost:8080/testJsonp?

callback=jQuery1102045087050669826567_1386230674292&_=1386230674293

相當於插入了這麼一個<script>標籤:

<script src="http://localhost:8080/testJsonp?callback=jQuery1102045087050669826567_1386230674292&_=1386230674293"></script>

server返回的資料是這樣子的:

jQuery1102045087050669826567_1386230674292({`name`:`abc`, `age`:18})

瀏覽器會執行直接執行這個JS函式。
所以。假設在callback函式的名字上做點手腳,能夠執行隨意的JS程式碼。所以說callback名字一定要嚴格過濾。
當然。callback函式的名字一般是程式自己控制的,可是不能排除有其他被利用的可能。
那麼callback函式的名字。怎樣過濾?應當僅僅同意合法的JS函式命名。用正則來匹配應該是這樣子的:

^[0-9a-zA-Z_.]+$

正則可能比較慢。能夠寫一個函式來推斷:

	static boolean checkJSONPCallbackName(String name) {
		try {
			for (byte c : name.getBytes("US-ASCII")) {
				if ((c >= `0` && c <= `9`) || (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) || c == `_`)
				{
					continue;
				} else {
					return false;
				}
			}
			return true;
		} catch (Throwable t) {
			return false;
		}
	}


實際上對callback函式名字進行嚴格檢驗還有其他的一個優點,就是防範了非常多UTF-7編碼攻擊。

由於UTF-7編碼的頭部都是帶有特殊字元的,如”+/v8″,”+/v9″,這樣就過濾掉非法編碼的請求了。

jsonp的請求的驗證

jsonp在使用的時候,還有easy犯的錯誤是沒有驗證使用者的身份。


第一。操作是否是使用者自己提交的,而不是別的網頁用<script>標籤,或者用<form>提交的

所以要檢查request的refer,或者驗證token。這個實際是CSRF防護的範疇。可是非常easy被忽略。


第二,要驗證使用者的許可權。

非常多時候。可能僅僅是驗證了使用者是否登入 。卻沒有細緻推斷使用者是否有許可權。

比方通過JSONP請求改動了別的使用者的資料。

所以說,一定要難證來源。推斷refer。驗證使用者的身份。

到烏雲上搜尋。能夠找到不少類似的漏洞,都是由於沒有嚴格驗證使用者的許可權。http://www.wooyun.org/searchbug.php?q=jsonp

JSON hijacking

在JS裡能夠為物件定義一些setter函式。這種話就存在了能夠利用的漏洞。



比方在瀏覽器的JS Console裡執行:

window.__defineSetter__(`x`, function() {
alert(`x is being assigned!`);
});
window.x=1;

會非常奇妙地彈出一個alert窗體,說明我們定義的setter函式起作用了。



結合這個,當利用<script>標籤請求外部的一個JSON API時,假設返回的是陣列型,就能夠利用竊取資料。



比方有這種一個API:

http://www.test.com/friends

返回的資料是JSON Array:

[{`user`:`test01`,`age`:18},{`user`:`test02,`age`:19},{`user`:`test03`,`age`:20}]

在攻擊頁面上插入下面的程式碼,就能夠獲取到使用者的全部的朋友的資訊。

<script>
  Object.prototype.__defineSetter__(`user`,function(obj)
      {alert(obj);
    }
  );
</script>
<script src="http://www.test.com/friends"></script>

這個漏洞在前幾年非常流行,比方qq郵箱的一個漏洞:http://www.wooyun.org/bugs/wooyun-2010-046

如今的瀏覽器都已經修復了,能夠下載一個Firefox3.0版本號來測試下。眼下的瀏覽器在解析JSON Array字串的時候。不再去觸發setter函式了。

但對於object.xxx 這種設定。還是會觸發。

IE的utf-7編碼解析問題

這個漏洞也以前非常流行。

利用的是老版的IE能夠解析utf-7編碼的字串或者檔案,繞過server的過濾。舉個烏雲上的樣例:http://www.wooyun.org/bugs/wooyun-2011-01293

有這種一個jsonp呼叫介面:

http://jipiao.taobao.com/hotel/remote/livesearch.do?callback=%2B%2Fv8%20%2BADwAaAB0AG0APgA8AGIAbwBkAHkAPgA8AHMAYwByAGkAcAB0AD4AYQBsAGUAcgB0ACgAMQApADsAPAAvAHMAYwByAGkAcAB0AD4APAAvAGIAbwBkAHkAPgA8AC8AaAB0AG0APg

url decoder之後是:

http://jipiao.taobao.com/hotel/remote/livesearch.do?callback=+/v8 +ADwAaAB0AG0APgA8AGIAbwBkAHkAPgA8AHMAYwByAGkAcAB0AD4AYQBsAGUAcgB0ACgAMQApADsAPAAvAHMAYwByAGkAcAB0AD4APAAvAGIAbwBkAHkAPgA8AC8AaAB0AG0APg

由於jsonp呼叫是直接返回callback包裝的資料。所以實際上。上面的請求直接返回的是:

+/v8 +ADwAaAB0AG0APgA8AGIAbwBkAHkAPgA8AHMAYwByAGkAcAB0AD4AYQBsAGUAcgB0ACgAMQApADsAPAAvAHMAYwByAGkAcAB0AD4APAAvAGIAbwBkAHkAPgA8AC8AaAB0AG0APg-(呼叫結果資料)

IE做了UTF-7解碼之後資料是這樣子的:

<htm><body><script>alert(1);</script></body></htm>(呼叫結果資料)


於是。就執行了XSS。
另外用IFrame也是能夠的。

可是我在IE8上測試,url的字尾須要是html才會觸發。

IE把沒有宣告返回Content-Type的請求當做了”text/html”型別的。然後解析就有問題了。僅僅要server端顯式設定了Content-Type為”application/json”,則IE不會識別編碼,就不會觸發漏洞。所以說server端的Content-Type一定要設定對。雖然設定之後除錯有點麻煩。可是卻大大提高了安全性。

JSON格式設定為:”application/json”

JavaScript設定為:”application/x-javascript”

JavaScript另一些設定為:”text/javascript”等,都是不規範的。


其他的一些東東

MongoDB注入

這個實際上就是JSON注入,簡單的字串拼接。可能會引發各種資料被改動的問題。

JSON解析庫的問題

有些JSON庫解析庫支援迴圈引用,那麼能否夠構造特別的資料,導致其解析失敗?從而引起CPU使用過高,拒絕服務等問題?

FastJSON的一個StackOverflowError Bug:

https://github.com/alibaba/fastjson/issues/76

有些JSON庫解析有問題:

http://www.freebuf.com/articles/web/10672.html

JSON-P

有人提出一個JSON-P的規範,可是貌似眼下都沒有瀏覽器有支援這個的。

原理是對於JSONP請求。瀏覽器能夠要求server返回的MIME是”application/json-p”,這樣能夠嚴格校驗是否合法的JSON資料。


CORS(Cross-Origin Resource Sharing)

為了解決跨域呼叫的安全性問題。眼下實際上可用的方案是CORS:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

http://www.w3.org/TR/cors/

原理是通過server端設定同意跨域呼叫,然後瀏覽器就同意XMLHttpRequest跨域呼叫了。

CORS能夠發起GET/POST請求。不像JSONP。僅僅能發起GET請求。

預設情況下,CORS請求是不帶cookie的。

我個人覺得,這個方法也非常蛋疼。一是須要server配置。二是協議複雜。瀏覽器假設不能確定是否可以跨域呼叫。還要先進行一個Preflight Request。。

實際上,即使server不同意CORS,XMLHttpRequest請求實際上是傳送出去,而且返回資料的了。僅僅是瀏覽器沒有讓JS環境拿到而已。

另外。我覺得有第二種資料洩露的可能:黑客可能控制了某個路由,他不能任意抓包,可是他能夠在回應頭裡插入一些特別的頭部。比方:

Access-Control-Allow-Credentials: true 

那麼。這時XMLHttpRequest請求就是帶cookie的了。

最初的問題

回到最初的問題:

  • 為什麼XMLHttpRequest要遵守同源策略?
即使XMLHttpRequest是不帶Cookie的,也是有可能造成資料洩露的。比方內部站點是依據IP限制訪問的,假設XMLHttpRequest不遵守同源策略,那麼攻擊者能夠在使用者瀏覽網頁的時候,發起請求,取得內部站點資料。
  • XMLHttpRequest 請求會不會帶cookie?
同域情況下會。不同域情況下不會。假設server設定Access-Control-Allow-Credentials: true ,也是能夠跨域帶Cookie的。
  • <script scr=”…”> 的標籤請求會不會帶cookie?
會。
  • 向一個其他域名的站點提交一個form,會不會帶cookie?
會。

總結:

  • 禁止手動拼接JSON字串。一律應當用JSON庫輸出。也不應使用自己實現的ObjectToJson等方法,由於可能有各種沒有考慮到的地方。

  • jsonp請求的callback要嚴格過濾,僅僅同意”_”,0到9。a-z, A-Z,即合法的javascript函式的命名。
  • jsonp請求也要推斷合法性,比方使用者是否登陸(這點非常easy被忽略)。
  • 設定好Content-Type(這點對於除錯不方便。可是提高了安全性)。

  • 以jsonp方式呼叫第三方的介面。實際相當於引入了第三方的JS程式碼,要謹慎。
本文轉自mfrbuaa部落格園部落格,原文連結:http://www.cnblogs.com/mfrbuaa/p/5028403.html,如需轉載請自行聯絡原作者


相關文章