發新話題
打印

[轉貼] 安全檢測Java Web應用網站漏洞

安全檢測Java Web應用網站漏洞

web開發應用程序(網站),是目前應用最廣泛的程序。但是開發者的水平參差不齊,導致了各種各樣web漏洞的出現。本文站在分層架構的角度,分析一下如何在java web程序中找到可能出現的種種漏洞。
    -----------代碼來自某ASP SHELL-----------
    Function GetFileSize(size)
    Dim FileSize
    FileSize=size / 1024
    FileSize=FormatNumber(FileSize,2)
    If FileSize < 1024 and FileSize > 1 then
    GetFileSize="<font color=red>"& FileSize & "</font> KB"
    ElseIf FileSize >1024 then
    GetFileSize="<font color=red>"& FormatNumber(FileSize / 1024,2) & "</font> MB"
    Else
    GetFileSize="<font color=red>"& Size & "</font> Bytes"
    End If
    End Function
    ----------------------------------------  


        如果客戶的需求變了,要求頁面不能使用「<font color=red>」等標籤,全部應用「CSS」顯示。掛了,把程序員召喚出來,一個一個的改吧。。。注意,這裡強調下,特指「召喚程序員」才能改,如果是學美工的,只會HTML、JS、CSS,完了,這活還幹不成。而這些只是簡單的頁面修改,如果客戶今天說,MYSQL服務器承擔不了這個數據量,要掛Oracle,可憐的程序員就要在一片一片的代碼海洋裡尋找執行SQL語句的代碼,而每一個文件都可能存放著SQL語句,意味著每一個文件都可能在受SQL注入的威脅。

         而JSP採用MVC模式分層架構進行開發,就可以把所有的文件分開,根據其用途,分別放在不同的文件夾下(分層),每個文件夾下的文件只負責自己的事情。例如數據訪問層的代碼就放在數據訪問層的文件夾下,業務邏輯層的代碼也都放在自己的文件夾下,當顯示層(這一層是為了把最終的運算結果顯示給用戶看)的需求發生變化,就像前面的客戶需求,我們只要修改這一層的文件就是了,其他層的代碼根本不需要動,而修改者也不需要懂得其它層的代碼。

    代碼分層了,意味著漏洞也在跟著分層,我們尋找JSP漏洞的思路也要跟著分層,才能與時俱進。

        下面在講述尋找漏洞的過程中,本文就拿一個簡單的分層架構例子來做樣板。樣板程序的名稱為「XX文章系統」,系統使用了STRUTS框架,和安全有關的層分為:

     「DB層」,這一層存放了鏈接數據庫的字符串,以及JdbcTemplate類,直接訪問數據庫。因為在java中,執行SQL語句的函數按照返回值可以分為三類,所以在這一層定義了JDBC模版類(JdbcTemplate),每一次使用操作數據庫時都要執行這一層的三個方法其中一個。

    「DAO層(Data Access Object數據訪問對像層)」,從安全角度上看,這一層存放了SQL語句(並不執行SQL語句,語句傳給DB層執行)。這一層調用「DB層」訪問數據庫,它只知道「DB層」的存在,不知道數據庫的存在。

    「SERVICE層」,業務邏輯層,因為一個業務的實現,並不是一次數據庫訪問就可以完成的,所以這一層通過N次調用「DAO層的方法」實現業務邏輯,它只知道「DAO層」的存在,不知道「DB層」和數據庫的存在。
「ACTION層」,調用業務邏輯層,根據返回的結果,控制JSP頁面顯示。它只知道業務層的存在。這一層是入侵者的攻擊平台。

    「Form層」,把用戶POST提交的信息封裝成Form對象,經過驗證後提交給ACTION層處理。

    「JSP層」(顯示層),這一層是最終顯示給用戶看的頁面,同時也是入侵者的攻擊平台。

         用戶通過訪問ACTION層,自動會發生:「ACTION調用SERVICE,SERVICE調用DAO,DAO調用DB,DB執行SQL語句返回結果給DAO,DAO返回給SERVICE,SERVICE返回給ACTION,ACTION把數據顯示到JSP裡返回給用戶」。

    有了樣板,我們來分析這套程序中可能出現的各種web漏洞。

    1、SQL注入漏洞

        從SQL注入漏洞說起吧,在web漏洞裡,SQL注入是最容易被利用而又最具有危害性的。怎麼快速的找到呢?先分析流程,就拿用戶查看文章這個流程為例:用戶訪問一個action,告訴它用戶想看ID為7的文章,這個action就會繼續完成前面所說的流程。

        如果是ASP程序,這就是最容易產生問題的時候,ASP是弱類型,接到參數後不需要轉換類型,就和SQL語句連加起來。但是JSP就不一樣,JSP是強類型的語言,接受有害的參數後:對於GET請求(直接在地址欄訪問頁面),如果這裡要的是int型,即使不懂安全的程序員,也會把它(文章的ID)立刻轉換成int,因為這裡轉換後在後面的處理中會更容易操作,而這時程序就出錯了;對於POST請求,如果這裡要的是int型,程序會在把它封裝成Form對像時,因為自動要進行類型轉化,同樣發生錯誤,這兩種錯誤發生後,根本不會訪問後面的流程就跳出了,或許這就是JSP天生的安全性。所以,通常提交的變量是int時,不會發生問題,問題會出現在string參數這裡,如果要查看某用戶的信息,程序可能會讓你提交如下參數:showuser.do?    username=kxlzx。問題來了,因為這裡是string類型,所以不懂安全的程序員頂多會判斷一下是不是空,就連加成為SQL語句。有漏洞的程序大概會寫成這個樣子:

    ACTION的代碼: showuser.do
    String username = null;
    username = request.getParameter("username");
    Service service = new Service();
    service.findByUsername(username);
    得到參數後調用service,service層直接交給了Dao層,dao的代碼:
    public Object findByUsername(String username)
     {
    JdbcTemplate jt=new JdbcTemplate();
    String sql = "select * from Users where username=』"+username"』";
    List list = jt.query(sql);
    ...................
     }  

       dao調用了DB層的JdbcTemplate,把SQL語句拼好後,傳給了JdbcTemplate去執行。不用再看這裡的JdbcTemplate,就可以知道裡面的代碼使用了Statement的executequery()方法執行,導致了SQL注入。

       分析了這麼半天,有讀者會問:「難道我要費這麼大的力氣才能找到漏洞麼?」。的確,通常在ASP程序裡找注入時的思路就是這樣子,但是我們現在是在使用了開發模式分層架構的JSP程序裡,應該按照分層架構的思維去找漏洞。在回答這個問題之前,我們還得繞個彎子,看看怎麼在這裡預防SQL注入(java始終都是這麼優美,它不會直接告訴你答案,而是一層一層的讓你撥開雲霧)。

        剛才分析流程,是從正向分析的,從用戶輸入到產生漏洞,我們在防禦的時候,不妨倒過來看看,從DB層入手。JdbcTemplate調用執行SQL語句,可以有兩個類供我們選擇,一個是Statement,另一個就是預處理的Statement,兩者有著效率上和安全上的顯著差別。在效率上,只要數據庫支持預處理技術(sqlserver,mysql,oracle等都支持,只有少數access等不支持),就會在大量執行SQL語句時增加速度;在安全上,使用預處理,會把接受的參數也經過預處理,從而不會作為SQL語句的一部分執行,而是僅僅作為SQL語句中的參數部分內容被執行。一旦DB層使用了預處理,DAO層的SQL語句也會發生變化,成為這個樣子:
    public Object findByUsername(String username)
     {
    JdbcTemplate jt=new JdbcTemplate();
    String sql = "select * from Users where username=?";
    List list = jt.query(sql,new Object[1]{username});
    ...................
    }  

        這樣,SQL注入就和我們的程序幾乎無關了,注意我說的是幾乎,而不是全部。知道了怎麼防禦,於是一切在這裡變的簡單極了,我們應該直接去DB層找到JdbcTemplate,看看代碼,一旦使用了Statement,很好,這個系統十有八九有漏洞,下面要做的是使用Editplus搜索「request.getParameter」。沒有使用預處理的系統,可能會在action層進行防禦,對參數過濾,但總有防禦不到的時候,因為戰線拉的太長了,每一個action裡都可能接受參數並存在漏洞。

        還有一種情況,系統一部分使用了預處理,一部分沒有,這樣的情況可能是因為項目趕的比較倉促,人員沒有經過正規培訓,最後艱難的整合到了一起。這種情況也好辦,直接在DAO層搜索("』)或(』"),這些符號用於和字符串變量連加,拼SQL語句,肯定是這些語句之後的代碼使用了Statement。然後再往上層找,看看哪個action調用了這個有問題的dao類,也可能發生SQL注入。
       即使系統使用了預處理,別忘了,程序給客戶使用後,客戶有權利去擴展的。程序拿到客戶那裡,客戶有了新的需求,而這個需求又不大,很可能不願意花錢重新僱人來實現擴展功能,在這個時候也可能出現問題,客戶使用自己的程序員擴展AJAX功能,這個程序員因為怕出問題,不敢動源程序,就在web.xml裡加了一個servlet,這個servlet直接去調用conn。可怕的事情發生了。所以,我們的搜索漏洞規則中又加上了一條,在非dao層的文件中,搜索「select,update,delete」等字符串。

    2、暴露程序信息漏洞

        這個漏洞是怎麼來的呢?我們需要從異常說起。有經驗的入侵者,可以從JSP程序的異常中獲取很多信息,比如程序的部分架構、程序的物理路徑、SQL注入爆出來的信息等,這個漏洞很容易防禦,卻很難快速定位漏洞文件。出現這樣漏洞的時候,通常是我們在寫代碼的時候,少了一些可能性的考慮而導致的。這樣的問題都是經驗造成的,而尋找漏洞也要通過經驗加運氣(要有仙緣。。。),我個人技術有限,就不多說了。防禦的方法就是在程序中加上「Exception層」,自定義異常,把系統產生的異常統統包裝起來,不要放過任何一個可能產生異常的地方。像騰訊的異常就包裝的很好「對不起,今天的註冊人數已經達到十萬,請您明天再來。。。」,廢話,日註冊量都十萬,還讓不讓人活啦,鐵定是程序發生了異常,不敢讓各位大大們看到真實的面孔。

   3、AJAX暴露出來的漏洞

        前面講SQL注入的時候說過的例子就是一個典型的情況,因為大多數網站不是在開發時就擁有Ajax技術的,都是後來看大家都用了,趕時髦加上。但是在加上的同時沒有意識到,在web上增加一個文件,就等於擴展了一點攻擊面。

    4、業務邏輯漏洞

        這個詞看起來挺抽像的,他和「暴露程序信息漏洞」有很多共同點,看名字就知道,應該是存在於業務邏輯層(service層)的漏洞。這樣的漏洞都和程序的運行邏輯有關,舉一個例子。

    5、XSS漏洞

        這個漏洞也影響深遠,想要發現這樣的漏洞,除了在頁面上進行測試外,還要從流程上入手。用戶輸入有害信息後,信息保存到數據庫,從數據庫中讀出來丟給用戶時產生漏洞。也就是說我們有兩個過程可以攔截,就是保存到數據庫時,和從數據庫讀出來後交給用戶時。最快的方法是直接打開數據庫查看數據,如果數據沒有經過編碼直接放進了數據庫,那麼可能性就有了一半。剩下的一半更簡單,在action層搜索轉碼常用的字符,如果沒有,就很容易發現漏洞。即使有,也不用著急,在action層裡慢慢找,很可能還有漏網之魚。

    6、頁面層的邏輯漏洞

        此類漏洞涵蓋面很大,包括「暴露不該暴露的數據」、「權限控制的不精確」、「方便客戶的同時存在安全隱患」等等。這類漏洞就只能靠著自己的經驗,使用系統的每一個細小功能來尋找。我給出幾個例子供大家參考。

       例1,「找回密碼」功能,這個功能是為了方便客戶的,可以通過一些客戶之前確認的信息而獲得客戶的身份驗證。有些程序的邏輯是這樣的,「請輸入VIP用戶名」--(判斷VIP用戶不存在)--「請輸入提示問題答案」--顯示用戶密碼或提供修改密碼的鏈接。第一步,入侵者可以獲得VIP用戶名是否存在,進而獲取密碼,第二步,一旦用戶留的問題比較弱智,就等於告訴別人自己的密碼。

        例2,用戶列表功能,試想,普通用戶閒著沒事看用戶列表做什麼?這個功分明是為了方便入侵者搜集用戶名的。

        例3,不要把防護交給JS,誰告訴我們使用JS可以防止用戶做某些操作的?這簡直是陷阱,JS只能防護普通用戶而已,永遠不要相信客戶端提交上來的數據,我見到有人的系統在限制上傳文件時,僅僅在JS裡做了防護,這不是自欺欺人麼?

        Web漏洞層出不窮,即使不存在以上漏洞,也別高興的太早,我們現在使用的技術,只能防範目前上了檯面的攻擊,或許今天你的系統固若金湯,明天就出現了新的攻擊方法。

TOP

發新話題