C# 参数化查询与SQL注入

C# 参数化查询与SQL注入,c#,asp.net,C#,Asp.net,我是Asp.net新手,我刚刚开始学习课程。我最近创建了一个类,它将为我处理大多数SQL查询,这样我就不必在所有文件上重复创建新连接 我创建的其中一个方法接受SQL查询作为参数并返回结果。我知道我应该使用参数化查询来避免SQL注入。我的问题是,当我将查询作为字符串参数传递时,如何才能做到这一点 例如,我将调用一个方法: public static DataTable SqlDataTable(string sql) { using (SqlConnection conn = new Sq

我是Asp.net新手,我刚刚开始学习课程。我最近创建了一个类,它将为我处理大多数SQL查询,这样我就不必在所有文件上重复创建新连接

我创建的其中一个方法接受SQL查询作为参数并返回结果。我知道我应该使用参数化查询来避免SQL注入。我的问题是,当我将查询作为字符串参数传递时,如何才能做到这一点

例如,我将调用一个方法:

public static DataTable SqlDataTable(string sql)
{
    using (SqlConnection conn = new SqlConnection(DatabaseConnectionString))
    {
        SqlCommand cmd = new SqlCommand(sql, conn);
        cmd.Connection.Open();
        DataTable TempTable = new DataTable();
        TempTable.Load(cmd.ExecuteReader());
        return TempTable;
    }
}
从另一个文件中,我想使用如下方法:

DataTable dt = new DataTable();

dt = SqlComm.SqlDataTable("SELECT * FROM Users WHERE UserName='" + login.Text  + "' and Password='" + password.Text + "'");

if (dt.Rows.Count > 0)
{
   // do something if the query returns rows
}

这是可行的,但仍然容易受到注射的影响,对吗?有没有一种方法可以将变量作为参数传递给字符串?如果我为查询创建一个新的SQLCommand对象并使用Parameters.AddWithValue,我知道我可以做到这一点,但我希望所有SQL命令都在单独的类中。

我的建议:使用orm。从现在起几天内有很多选择

您尝试做的事情具有完美的逻辑意义,我可以理解您为什么会实现此实现。但是,您尝试执行的操作非常危险,而且作为ASP.NET的新手,您可能没有意识到还有许多其他选项可供您选择,这些选项使您的数据管理变得更加简单和安全

@iamkrillin暗示了这样一种技术——对象关系映射(ORM)。NET框架实际上对名为的ORM提供了一流的支持。我相信他建议你研究ORM的原因是因为你的设计在原则上与ORM的工作方式非常相似。它们是抽象类,表示数据库中的表,可以使用LINQ轻松查询这些表。LINQ查询会自动参数化,从而减轻管理查询安全性的压力。它们动态生成SQL(与向数据访问类传递字符串的方式相同),并且在返回数据(数组、列表,您可以命名)的方式上更加灵活


然而,ORM的一个缺点是,它们的学习曲线非常陡峭。一个更简单的选择(虽然比EF稍早一点)是使用类型化数据集。类型化数据集比独立的ORM更容易创建,并且通常更容易实现。虽然不像ORM那样灵活,但它们以一种简单、安全且已经解决的方式完全完成了您想要做的事情。幸运的是,当ASP.NET第一次出现时,培训视频主要集中在类型化数据集上,因此有各种高质量的视频可以帮助您快速启动和运行。

您走的是正确的道路,我也确实完成了您为我自己寻找的工作。但是,我并没有将字符串传递给函数,而是传递了一个SQL命令对象。。。这样,您就可以正确地构建所有命令和参数,然后说。。。来,开始运行,准备好了。差不多

public static DataTable SqlDataTable(SqlCommand cmd)
{
    using (SqlConnection conn = new SqlConnection(DatabaseConnectionString))
    {  
        cmd.Connection = conn;   // store your connection to the command object..
        cmd.Connection.Open();
        DataTable TempTable = new DataTable();
        TempTable.Load(cmd.ExecuteReader());
        return TempTable;
    }
}

public DataTable GetMyCustomers(string likeName)
{
    SqlCommand cmd = new SqlCommand();
    cmd.CommandText = "select * from SomeTable where LastName like "@someParm%";
    cmd.Parameters.Add( "whateverParm", likeName );  // don't have SQL with me now, guessing syntax

    // so now your SQL Command is all built with parameters and ready to go.
    return SqlDataTable( cmd );
}
这是可行的,但仍然容易受到注射的影响,对吗

是的,您的代码非常容易受到SQL注入的攻击

我知道我应该使用参数化查询来避免SQL注入

哦,绝对是的

我的问题是,当我将查询作为字符串参数传递时,如何才能做到这一点

您不应该将查询作为字符串参数传递。相反,您应该将查询作为字符串参数传递,其中包含占位符和这些占位符的值:

public static DataTable SqlDataTable(string sql, IDictionary<string, object> values)
{
    using (SqlConnection conn = new SqlConnection(DatabaseConnectionString))
    using (SqlCommand cmd = conn.CreateCommand())
    {
        conn.Open();
        cmd.CommandText = sql;
        foreach (KeyValuePair<string, object> item in values)
        {
            cmd.Parameters.AddWithValue("@" + item.Key, item.Value);
        }

        DataTable table = new DataTable();
        using (var reader = cmd.ExecuteReader())
        {
            table.Load(reader);
            return table;
        }
    }
}
公共静态数据表SqlDataTable(字符串sql,IDictionary值)
{
使用(SqlConnection conn=newsqlconnection(DatabaseConnectionString))
使用(SqlCommand cmd=conn.CreateCommand())
{
conn.Open();
cmd.CommandText=sql;
foreach(值中的KeyValuePair项)
{
cmd.Parameters.AddWithValue(“@”+item.Key,item.Value);
}
DataTable=新的DataTable();
使用(var reader=cmd.ExecuteReader())
{
表1.负载(读卡器);
返回表;
}
}
}
然后像这样使用你的函数:

DataTable dt = SqlComm.SqlDataTable(
    "SELECT * FROM Users WHERE UserName = @UserName AND Password = @Password",
    new Dictionary<string, object>
    {
        { "UserName", login.Text },
        { "Password", password.Text },
    }
);

if (dt.Rows.Count > 0)
{
   // do something if the query returns rows
}
DataTable dt=SqlComm.SqlDataTable(
“从用户名=@UserName和密码=@Password的用户中选择*”,
新词典
{
{“用户名”,login.Text},
{“Password”,Password.Text},
}
);
如果(dt.Rows.Count>0)
{
//如果查询返回行,请执行某些操作
}

如果您不想使用蹩脚的ORM式实体框架。你挑吧,有很多。NHibernate,LightSpeed。还有数以十亿计的微型生物,如PetaPoco,Massive,ServiceStack.OrmLite,DarinDimitrov,为什么不好?你能用任何资源/文章/测试等来支持你的论点吗?@Emin,当然,看看StackOverflow上关于人们在实体框架方面有问题的问题的数量。现在看看StackOverflow上关于人们与其他ORM有问题的问题的数量。然后数一数。数字越大,情况就越糟,相信我,EF在这场竞争中处于领先地位:-)但我甚至不明白我们为什么要在这里争论ORM。OP有一个与ORM无关的特定问题。这是关于如何用ADO.NET避免SQL注入攻击的。@DarinDimitrov我相信你。我最近开始在一个简单的项目中使用它,并在考虑将来在更高级的项目中可能会遇到什么问题。既然你反对EF,我想你可能会给我一些关于它的信息,这就是为什么我问它。@ajax81这是一个很大的帮助。非常感谢您提供更多信息的链接。我肯定会深入研究所有这些问题。为什么要使用内联查询?您可以使用存储过程来避免sql注入。存储过程是预执行的sql查询。因此,它将比内联查询运行得更快。只是建议使用存储过程。曾经去过一次,我强烈建议您深入学习和/或