C# Sql注入参数化查询

C# Sql注入参数化查询,c#,sql,database,sql-injection,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(quer

我需要实现下面的功能

     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注入来实现替换

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

例如,我有另一个查询
从[party details]中选择*,其中address=@address和name=@anme
,为此,我需要再次编写一个函数,其中使用
com.Parameters.AddWithValue(“@address”,address”)

com.Parameters.AddWithValue(“@name”,name),这只是浪费时间。查询可以有不同的参数编号,我需要的函数不依赖于查询中的参数编号。

您需要将其转换为以
名称作为参数的存储过程


这里是开始阅读的好地方

我建议将可选参数和条件逻辑相结合。这是总的想法。我不担心语法

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
名为“GetAllPartyDetails”的函数不应接受sql字符串作为参数。这样一种方法的目的是将应用程序其余部分了解或关心sql的需要抽象出来,只需提供一个独立于数据库实现的参与方详细信息源。它应该接受参与方的名称作为参数,但是如果您需要接受sql查询,那么您在错误的位置构建sql

您需要的是一个方法,该方法不仅可以从GetPartyDetails()调用,还可以从需要特定数据源数据的其他方法调用。如果您要在GetPartyDetails之外构建查询字符串,则需要对其进行重新构建

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

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

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);
          }
       }
    }
}
如果您有另一个带有参数的查询,您可以这样调用它:

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);
    });
}
不接受任何参数的方法如下所示:

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);
    });
}
public IEnumerable GetAllPartyDetails()
{
return GetData(“select*from[Party Details]”,p=>{},row=>
{
row.GetString(0);
});
}
这有点尴尬,但这就是重点。您希望人们慎重考虑不使用参数,因此他们会情不自禁地选择正确的方法

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

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


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

为什么不能准确地使用第一个示例?如何创建这些传递给函数的查询字符串?Ramhound检查我的编辑您将了解为什么不能准确地使用第一个示例。但这些查询仅在调用函数的值中预定义参数是在运行时插入的。此外,您没有正确关闭连接。如果查询引发异常,则无法调用.Close(),您将挂起它。通过这种方式锁定数据库是可能的!您的代码将实现我需要的功能,但另一方面,它也会降低性能,因为我需要检查所有参数的条件是否正确。正确,但我们可能谈论的是纳秒。如果很多用户都要使用纳秒,那么它就很重要了。不过,还是要感谢您的方法。Joel,我的第一个示例和您的答案之间是否有性能差异。@HotCoolStud可能有,因为您使用了.AddWithValue()。AddWithValue()必须猜测参数类型。有时它会出错,这可能意味着数据库没有使用好的索引,或者必须对表中的每一行进行昂贵的转换。除了这个怪癖,他们应该差不多一样。但在这种情况下,正确性胜过性能。您当前的设计不正确。那么com.Parameters和com.Parameters.AddWithValue()com.Parameters.Add()之间的区别是什么呢?需要您指定参数类型。AddWithValue()尝试猜测参数类型。在某些情况下,AddWithValue()的猜测是错误的,这些情况下可能会有严重的性能损失。最后@joel我很清楚我的所有疑问,感谢您提供这一重要信息。