Sql Injection参数化查询

时间:2013-05-09 16:12:20

标签: c# sql database sql-injection

我需要实现以下功能

     public PartyDetails GetAllPartyDetails(string name)
    {
        try
        {
            String query = "select * from [Party Details] where name=@name ";
            pd = new PartyDetails();
            com = new SqlCeCommand(query, con);
            com.Parameters.AddWithValue("@name", name);
            con.Open();
            sdr = com.ExecuteReader();
            while (sdr.Read())
            {

                pd.name = sdr.GetString(0);

            }
            con.Close();
            return pd;
        }

        catch (Exception e)
        {
            con.Close();
            throw e;
        }
    }

但是这个函数对我来说效率不高,因为我不需要仅仅因为查询的改变而编写不同的函数代码。

现在我需要的是

  public PartyDetails GetAllPartyDetails(string query)
        {
            try
            {
            pd = new PartyDetails();
            com = new SqlCeCommand(query, con);
            con.Open();
            sdr = com.ExecuteReader();
            while (sdr.Read())
            {
                pd.name = sdr.GetString(0);
            }
            con.Close();
            return pd;
        }
        catch (Exception e)
        {
            con.Close();
            throw e;
        }
    }

但它增加了sql注入的风险,因为它没有使用com.Parameters.AddWithValue("@name", name);。是否可以通过调用函数来停止sql注入来实现这一点。

对于那些不理解我的问题的人

例如,我有另一个查询select * from [party details] where address=@address and name=@anme,为此,我需要再次编写一个函数,其中我使用com.Parameters.AddWithValue("@address", address); com.Parameters.AddWithValue("@name", name);,这只是浪费时间。查询可以有不同的参数,我需要的函数不依赖于查询中的参数。

3 个答案:

答案 0 :(得分:2)

名为“GetAllPartyDetails”的函数应该接受sql字符串作为参数。这样的方法的目的是抽象出应用程序的其余部分需要知道或关心sql,并简单地提供与数据库实现分开的派对详细信息的源。它应该接受派对的名称作为参数,但是如果你需要接受一个sql查询,你就是在错误的地方构建你的SQL。

您需要的是一种方法,不仅可以通过GetPartyDetails()以及其他需要来自特定数据源的数据的方法以通用方式调用。如果您在GetPartyDetails之外构建查询字符串,则需要重新构建一点。

检索任何数据的方法应该是什么样的?当然它需要接受一个sql字符串。它还需要某种方式来接受参数信息。这可能就像键/值对的数组一样简单,但我更喜欢避免需要两次构建参数集的代码。此参数也应该是 required ,而不是可选或重载,以鼓励使用良好的参数。

我目前使用这种模式,我非常喜欢它:

private IEnumerable<T> GetData(string sql, Action<SqlParameterCollection> addParams, Func<IDataRecord, T> translate)
{
    using (var cn = new SqlConnection("connection string here"))
    using (var cmd = new SqlCommand(sql, cn))
    {
       addParams(cmd.Parameters);
       cn.Open();
       using (var rdr = cmd.ExecuteReader())
       {
          while (rdr.Read())
          {
              yield return translate(rdr);
          }
       }
    }
}

这符合我们的通用数据访问方法的所有目标。我不感兴趣的唯一部分是对翻译代表的需求,并没有那么大的损失,因为这是你必须在你的应用程序的某个级别编写的代码。如果不将每一行复制到该方法中的新对象,则可能会产生意外结果,因此我们需要一种方法将datarecord转换为业务对象。

你会这样称呼:

public string GetPartyDetailsByName(string name)
{
    return GetData("select * from [Party Details] where name=@name", p =>
    {
       p.Add("@name", SqlDbType.NVarChar, 50).Value = name;
    }, row =>
    {
       row.GetString(0);
    }).First();
}

如果您有另一个带参数的查询,您可以这样称呼它:

public string GetPartyDetailsByNameAddress(string name, string address)
{
    return GetData("select * from [Party Details] where name=@name and address=@address", p =>
    {
       p.Add("@name", SqlDbType.NVarChar, 50).Value = name;
       p.Add("@address", SqlDbType.NVarChar,200).Value = address;
    }, row =>
    {
       row.GetString(0);
    }).First();
}

不带任何参数的方法如下所示:

public IEnumerable<string> GetAllPartyDetails()
{
    return GetData("select * from [Party Details]", p => {}, row =>
    {
       row.GetString(0);
    });
}

这有点尴尬,但这就是重点。你希望人们故意关于不使用参数,所以他们不禁绊倒了正确的方式。

我知道您希望避免编写两种方法,但这是处理数据访问的正确方式。是的,有一个与数据库对话的方法,以帮助抽象出一些样板代码。但是为每个sql查询添加一个额外的方法仍然是正确的做法。

您不需要完全遵循我的GetData()方法:功能样式对某些人来说有点多。但是,您确实需要一个可以向数据库发送查询的唯一方法,并且此方法必须具有一些接受参数数据的机制。其他方法应该传递sql。这导致了注射问题。

您询问数据的每个问题都属于它自己的方法。理想情况下,这些方法聚集在一个类或一组类中,这些类聚集在一个项目中,用于更大的应用程序。

答案 1 :(得分:1)

我建议使用可选参数和条件逻辑的组合。这是一般的想法。我并不担心语法。

public PartyDetails GetAllPartyDetails(string name = string.Empty, 
string address = string.Empty)
{
String query="select * from [Party Details] where 1=1 ";

if (name != string.Empty)
{
query = query + " and name = @name";
code to add parameter
}
repeat for all arguments
rest of function

答案 2 :(得分:0)

您需要将此转换为以name作为参数的存储过程。

这是一个开始阅读的好地方。 http://msdn.microsoft.com/en-us/library/ff648339.aspx