参数化查询与SQL注入

本文关键字:SQL 注入 查询 参数 | 更新日期: 2023-09-27 18:21:24

我是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命令都在单独的类中。

参数化查询与SQL注入

这是有效的,但仍然容易受到注射的影响,对吧?

是的,您的代码极易受到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;
        }
    }
}

然后像这样使用你的功能:

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
}

您试图做的事情具有完美的逻辑意义,我可以理解您为什么会实现这个实现。然而,您尝试做的事情非常危险,而且作为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 );
}
我的建议是:使用orm。从现在开始,有很多选择