天天看點

通用分頁存儲過程真的有注入漏洞嗎?

     今天看了兩篇關于存儲過程SQL注入漏洞的文章:

     怎麼看怎麼覺的别扭,在我印象中存儲過程是不會存在注入漏洞的啊?起碼我目前的水準還不了解如何注入存儲過程。如果大家有注入的方法請指教。換句話說存儲過程本身并無注入漏洞,隻不過有漏洞大多都是因為程式漏洞導緻。

     我們來簡化下之前兩位園友讨論的分頁存儲過程,原代碼太長,我這裡呢寫一個針對一個單表查詢的存儲過程。建立一個使用者表,表結構如下:有三個字段,人員ID,姓名字段。

CREATE TABLE [dbo].[person](

    [id] [int] NULL,

    [last_name] [varchar](30) COLLATE Chinese_PRC_CI_AS NULL,

    [first_name] [varchar](30) COLLATE Chinese_PRC_CI_AS NULL

) ON [PRIMARY]

     然後寫一個查詢存儲過程(getPerson):作用,根據不同的條件讀取使用者資訊。

IF ( EXISTS ( SELECT    *

              FROM      sysobjects

              WHERE     id = OBJECT_ID(N'[dbo].[getPerson]')

                        AND OBJECTPROPERTY(id, N'IsProcedure') = 1 ) )

    BEGIN

        DROP PROCEDURE [dbo].[getPerson]

    END

Go

CREATE PROC getPerson

    @strWhere VARCHAR(100) = '' -- 查詢條件 (注意: 不要加 where)

AS

        DECLARE @strSQL VARCHAR(1000) -- 主語句

        SET @strSQL = 'select top 10 * from person where 1=1 '

    --如果存在條件,則加上

        IF @strWhere != ''

            BEGIN

                SET @strSQL = @strSQL + @strWhere          

            END

        PRINT ( @strSQL )

        EXEC ( @strSQL

            )

     查詢方式,根據使用者的姓來查詢。要想最終的存儲過程執行文法正确,同時不存在注入漏洞, 此時條件的正确格式是:and first_name like '%Jim''s dog%'。

    我們可以看到條件Jim's dog組裝成SQL後,中間的單引号一定要變成兩個。為了避免注入,我一般這樣處理SQL拼接的安全問題:在C#寫程式的時候應該這樣寫:

/// <summary>

        /// 屏蔽字元串中的特殊字元

        /// by minjiang 07-07-06

        /// </summary>

        public  string  SafeRequest(string str)

        {

            //定義要傳回的字元串

            string sReturn;

            //将要處理的字元串轉換為小寫字母

            str = str.ToLower();

            //定義特殊字元串

            string SQL_KILL = "'|and|exec|insert|select|delete|update|count|*|%

|chr|mid|master|truncate|char|declare|set|;|from|=|--|drop|<|>";

            char[] separator ={ '|' };

            string[] sql = SQL_KILL.Split(separator);

            for(int i=0;i<sql .Length ;i++)

            {

                //如果有特殊字元則将它替換成為空

                if(str.IndexOf (sql [i].ToString ().ToLower ())>-1)

                {

                    //把單引号替換成雙引号

                    if (sql[i].ToString() == "'")

                    { str = str.Replace("'", "''"); }

                    else

                    {

                        //把敏感字元替換成空

                        str = str.Replace(sql[i].ToString().ToLower(), "");

                    }

                }

            }

            sReturn = str;

            return sReturn;

        }

if(sUserName!="")

{

  strWhere +=" and first_name like'%"+this.SafeRequest(sUserName)+"%'"

}

     分頁存儲過程注入的機會: 上面的通用分頁存儲過程之是以會說存在SQL注入的機會,是因為通配符like後面的單引号,如果在後面參數中也出現單引号與like通配符後面的單引号相比對後,後面的内容就是SQL注入的内容了。此時我們可以寫一個過濾SQL特殊字元的方法,對特殊字元進行處理,可能根據自己的情況,選取相應過濾條件。最起碼要把使用者名中的單引号替換成雙引号。下面的寫法是不安全的:使用者名中有單引号,例如 :Jim's dog

  strWhere +=" and first_name like'%"+sUserName+"%'"

     我個人不太支援這種所謂高效的通用分頁存儲過程,理由:

     1:可閱讀性太差,整版的字元串,誰看着都不舒服。

     2:對應用程式有比較高的安全要求,稍不注意就會存在上面所說的注入漏洞。

     3:對多表的複雜查詢無能無力。如果強行應用,我想遠比單獨寫一個存儲過程來的麻煩。

     4:所謂通用,即大多數人都知道你這個存儲過程的大緻結構,這樣無疑給别有用心者更多可趁之機。

     總結:通用分頁存儲過程本身是沒有漏洞可言的,隻不過是程式的不嚴謹造成的注入機會。

    解決這種拼接SQL字元串可能帶來的隐患方案:

    1:盡量對輸入參數進行類型設定,能設定成數字型的一定要設成數字型。

    2:設定好參數的長度,一個字元串,例如姓名,一般不會超過20個字元。

    3:輸入的參數内容能删除空格的就最好利用Trim(),這樣,就算有SQL敏感字元,一旦SQL連接配接成一串,那也是不能夠正常注入。

    4:盡量過濾傳入的條件,起碼要把單引号替換成雙引号。

    5:嚴格設定資料庫使用者的權限,負責查詢的使用者,隻讓它具有讀的權限,這樣就算是注入成功,也不能造成緻命的後果。

具有插入權限的使用者,嚴格控制删除,更新的權限。而傭有删除權限的使用者,一般都傭有檢視權限,删除操作是很難存在SQL注入的。

注:

   歡迎大家發表意見!