none
SqlCommand.Parameter雖很安全 但不太好用 RRS feed

  • 問題

  •  

    日前看到論壇上某文章介紹LINQ的作用以及Injection的破壞性

    感覺很務實

    這的確是工程師對於SQL語法撰寫效率與安全性的兩難

    SqlCommand.ParameterAddWithValue()的方法

    被推薦是屬於可以克服Injection

    理由在於SqlCommand可以替使用者在SQL端宣告好[純量]變數

    讓使用者在使用SqlCommand.Parameter族群的方法時

    可以使用參數化的方式將網頁上的值納入SqlScript

    安全的避開危險的SQL關鍵字

     

    然而小弟對於Parameter實際上的用法以及其易遭遇的困難

    可能就不僅僅只是講師評斷的 [ 只多幾行的維護難度 ]

     

    例如

    string strCustomer = “TONY”;

    sqlCommand1.Parameter.AddWithValue(“@Customer” , strCustomer);

    string strTel = “123456789”;

    sqlCommand1.Parameter.AddWithValue(“@Tel”,strTel);

    ………

    上面的方法用的都很順

    完全沒有問題,特別適用於Insert Update 之類的

    要把值帶到TableField上的è絕對是非常的好用

     

    但是

    如果上述的資料用於Where,那可就比串字串SQL寫法

    累的

    ---------!!!

    且不易D  E  B  U  G  !!!!

     

    例如同例子

    我要用 不等於 的條件去找顧客與電話

    寫法可能是

    strSQL =

    “SELECT * FROM CUSTOMER WHERE Cus != @Customer AND Tel != @Tel”;

    SqlCommand sqlCommand1 = new SqlCommand(strSQL);

    string strCustomer = TextBox1.Text;

    sqlCommand1.Parameter.AddWithValue(“@Customer” , strCustomer);

    string strTel = TextBox2.Text;

    sqlCommand1.Parameter.AddWithValue(“@Tel”,strTel);

    …….

     

    如果TextBox.Text填妥了某人的電話與顧客姓名

    並不能等於撈出 “ Tel IS NULL” 或者 “Tel != ‘’ OR Tel IS NULL”

    它只會採用 Where Tel != ‘123456789’ 來處理

     

    即便光用手動寫這句

    sqlCommand1.Parameter.AddWithValue(“@Customer” , null);

    也直接被工具判斷出錯誤訊息

    只能 [       ] 不寫或寫在SQLScirpt上才有NULL效果us IS NULL ULLnd()RE Customer = @Customer

     

    因此底下的例子將會很難處理

    If(strTel == “”)

    {

            strWhere1 = (Tel != @Teltomer OR Tel IS NULL);

    }

    else

    {

            strWhere1 = (Tel != @Teltomer )

    }

    //那我假如想要TextBox是只撈非空白資料的

    //那我假如想要撈不是該值且IS NULL 也累

     

    至於不易Debug

    就是在於不能利用下中斷點的方式

    將陳述式以文字形式反白複製到SQL上去做查詢

    因為Parameter的陳述式充滿了 [ SQL端宣告好的變數 ] ,

    而非C#Vb.Net上變數的值

    因此加入監看式的方法就徹底無效

    即便放在SQL Server Studio 也是無效用

    雖說好像Visio studio可以支援SQLScript去跑監看式

    但那應該是專指套用store procedure

    畢竟不是以網頁的方式丟參數判斷處理資料的互動了

    何況

    è我私底下的埋怨

    èSQL語句並不物件導向 並不支援for迴圈類等好用的指令 而是用指標與while

    è太直譯的寫法,語法不口語

    èGUI且無IO類的語法可用

    但是該死的就是它的處理資料效能超強,不得不用它

    è撈連續資料採用指標下過濾資料 For迴圈一條一條判斷要不要來的有效率

    2008年6月1日 上午 08:47

解答

  • 如果你看過這個:

    http://www.microsoft.com/taiwan/msdn/columns/huang_jhong_cheng/LVSS.htm

     

    裡面有一段話:

     

    Code Snippet
    SQL Injection 至今仍然存在的原因很簡單,程式設計師的惰性慣性大而化之的個性,是導致 SQL Injection 存在於這個高安全性當道時代的最大原因。

     

     

     

    你列出的那些理由,只更反應出你有上面那一句話所說的那些個性,那我要為使用你寫的系統的公司祈禱,不要哪一天被 SQL Injection 所攻擊,然後你就會發現,災後重建要花的心力,往往大於你寫那些多出來的處理程式碼的數十倍到數百倍,讓你重建已經算萬幸了,如果公司直接開除加上用法律手段向你求償,看你要怎麼辦。

     

    不要認為我在危言聳聽,不信你可以試看看。

     

    Parameter 的除錯可以利用中斷點和實際組合 SQL 的方式來處理來做,而不是只有用什麼特定的方法才可以除錯,不試著用其他方法去達成原本監看式無法做到的,還是一個字:懶。

     

    2008年6月1日 上午 11:45
    版主

所有回覆

  • 移文至 資料存取(ADO.NET與LINQ) 討論區
    2008年6月1日 上午 11:29
  • 如果你看過這個:

    http://www.microsoft.com/taiwan/msdn/columns/huang_jhong_cheng/LVSS.htm

     

    裡面有一段話:

     

    Code Snippet
    SQL Injection 至今仍然存在的原因很簡單,程式設計師的惰性慣性大而化之的個性,是導致 SQL Injection 存在於這個高安全性當道時代的最大原因。

     

     

     

    你列出的那些理由,只更反應出你有上面那一句話所說的那些個性,那我要為使用你寫的系統的公司祈禱,不要哪一天被 SQL Injection 所攻擊,然後你就會發現,災後重建要花的心力,往往大於你寫那些多出來的處理程式碼的數十倍到數百倍,讓你重建已經算萬幸了,如果公司直接開除加上用法律手段向你求償,看你要怎麼辦。

     

    不要認為我在危言聳聽,不信你可以試看看。

     

    Parameter 的除錯可以利用中斷點和實際組合 SQL 的方式來處理來做,而不是只有用什麼特定的方法才可以除錯,不試著用其他方法去達成原本監看式無法做到的,還是一個字:懶。

     

    2008年6月1日 上午 11:45
    版主
  •  

    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using System.Data.SqlClient;
    using System.Data;
    using System.IO;
    using System.Text;

     

    /// <summary>
    /// SQL_ 的摘要描述
    /// 此類別為 Appathy 開發 , 開發時間為2008/05/31
    /// 請記念Appathy工程師之已故女友 鄭文惠
    /// </summary>
    public class SQL_

     {
        public SQL_()
     {
      //
      // TODO: 在此加入建構函式的程式碼
      //
     }
        public bool ConfirmFunction(DataSet DB)
        {
            try
            {
                if (DB.Tables[0].Rows.Count != 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                return false;
            }
        }
        public void FCREATE(string newFilesal)
        {
            File.WriteAllText(newFilesal, "", Encoding.Default);
        }
        public void FWRITE(string BU1 , string newFilesal)
        {
            File.AppendAllText(newFilesal, BU1, Encoding.Default);
        }
        public bool StrInsertUpdateDelete(string strSQL)
        {
           
            if (GetPrivateProfileString() == "")
            {
                return false;
            }

            try
            {
                SqlCommand scSQLCommend = new SqlCommand();
                SqlConnection scSQLConnection = new SqlConnection(GetPrivateProfileString());
                scSQLConnection.Open();
                scSQLCommend.ExecuteNonQuery();
                scSQLConnection.Close();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
        public bool ParamsInsertUpdateDelete(string strSQL,string[] strParameterNames,string[] strParametersValues)
        {
            if (strParametersValues.Length <= 0 ||
                strParameterNames.Length <= 0)
            {
                return false;
            }
            try
            {
                SqlCommand scSQLCommend = new SqlCommand(strSQL);
                SqlConnection scSQLConnection = new SqlConnection(GetPrivateProfileString());
                for (int index = 0; index < strParametersValues.Length; index++)
                {
                    scSQLCommend.Parameters.AddWithValue("@" + strParameterNames[index], strParametersValues[index]);
                }
                scSQLConnection.Open();
                scSQLCommend.ExecuteNonQuery();
                scSQLConnection.Close();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
        public bool ParamsInsertUpdateDelete(string strSQL, string strParameterNames, string strParametersValues)
        {
            try
            {
                SqlCommand scSQLCommend = new SqlCommand(strSQL);
                SqlConnection scSQLConnection = new SqlConnection(GetPrivateProfileString());

                scSQLCommend.Parameters.AddWithValue("@" + strParameterNames, strParametersValues);
                scSQLConnection.Open();
                scSQLCommend.ExecuteNonQuery();
                scSQLConnection.Close();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
        /// <summary>
        /// 取得聯線字串的方式先採WebConfig
        /// 後採Windows.INI檔
        ///
        /// </summary>
        /// <returns></returns>
        private string GetPrivateProfileString()
        {

            string strINI = "";
            StreamReader srINI = new StreamReader(@"C:\WINDOWS\WinINI.ini");

            try
            {
                strINI = ConfigurationManager.ConnectionStrings["cwsConnectionString"].ConnectionString;
                if (strINI != "")
                {
                    return strINI;
                }
               
                strINI += "Data Source=";
                while (true)
                {
                    strINI += srINI.ReadLine() + ";";

                    if (srINI.EndOfStream == true)
                    {
                        break;
                    }
                }
                strINI = strINI.Replace("User","User ID");
                strINI = strINI.Replace("dbname" , "Initial Catalog");
                strINI = strINI.Replace("paswd","Password");
                strINI = strINI.Replace("sername","Data Source");
                return strINI;
            }
            catch (Exception ex)
            {
                return "";
            }
            finally
            {
                srINI.Close();
                srINI.Dispose();
                srINI = null;
            } 

        }
        public DataSet StrGetDataSet(string strSQL)
        {
            try
            {
                DataSet DB = new DataSet();
                SqlDataAdapter sdaSQLDataAdapter = new SqlDataAdapter(strSQL, GetPrivateProfileString());
                sdaSQLDataAdapter.Fill(DB);
                return DB;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public DataSet ParamsGetDataSet(string strSQL, string[] strParameterNames, string[] strParametersValues)
        {
            try
            {
                DataSet DB = new DataSet();
                SqlConnection scSQLConnection = new SqlConnection(GetPrivateProfileString());
                SqlCommand scSQLCommend = new SqlCommand(strSQL,scSQLConnection);
               
                for (int index = 0; index < strParametersValues.Length; index++)
                {
                    scSQLCommend.Parameters.AddWithValue("@" + strParameterNames[index], strParametersValues[index]);
                }
                SqlDataAdapter sdaSQLDataAdapter = new SqlDataAdapter(scSQLCommend);
                sdaSQLDataAdapter.Fill(DB);
                return DB;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public DataSet ParamsGetDataSet(string strSQL, string strParameterNames, string strParametersValues)
        {
            try
            {
                DataSet DB = new DataSet();
                SqlConnection scSQLConnection = new SqlConnection(GetPrivateProfileString());
                SqlCommand scSQLCommend = new SqlCommand(strSQL, scSQLConnection);
                if (strParameterNames == "")
                {
                    //do nothing
                }
                else
                {
                    scSQLCommend.Parameters.AddWithValue("@" + strParameterNames, strParametersValues);
                }
                SqlDataAdapter sdaSQLDataAdapter = new SqlDataAdapter(scSQLCommend);
                sdaSQLDataAdapter.Fill(DB);
                return DB;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
    }

     

    這東西   希望大家喜歡

    寫的不怎樣  也沒啥高深的

    假如這樣會被法院關起來

    那就關起來吧

    我不打算寫了

    2008年6月1日 下午 12:05
  • 沒人強迫你寫,只是告訴你事情的嚴重性,如果這種話你聽不下去,只想聽 xx 可以做到不必寫程式就可以做到的功能的話,那等別人來講吧。

     

    另外,如果滿腦子只想著要怎麼不費吹灰之力寫出能滿足各種需求的程式,而不動手去實行或是怕麻煩又懶,那我想你真的不適合做程式設計和軟體開發這一行,轉行對你來說可能是最好的選擇。

    2008年6月1日 下午 12:18
    版主