参数化查询 vs SQL注入

7
我刚开始接触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命令都在单独的类中。

1
@HirenDhaduk:存储过程已经过时了!如果有可能,尽量不要使用它们。 - Fabian Bigler
令人难以置信的是,当使用纯粹的 ADO.NET 就已经具备编写适当且安全的代码所需的所有要素时,有些人建议使用 ORM 或存储过程来解决 SQL 注入问题。 - Darin Dimitrov
@HirenDhaduk 不,它们不是。但我不怪你。许多人认为它们是.. http://www.scarydba.com/2009/09/30/pre-compiled-stored-procedures-fact-or-myth/ - Subliminal Hash
3
我们并不建议使用ORM来解决注入攻击的问题。我们建议采用一种不需要处理此类问题的编程方式。 - Subliminal Hash
感谢大家的评论。由于我对 .net 不太熟悉,我一定会仔细研究所有建议的选项。再次感谢! - Cineno28
显示剩余6条评论
4个回答

15

这个方法虽然可行,但仍然容易受到注入攻击对吧?

是的,你的代码非常容易受到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
}

感谢帮助。大家提供了许多有用的信息,但我认为我会将这个答案标记为正确答案,因为它直接关系到我的原始目标。不过,我会研究所有建议,以更多地了解此过程。谢谢! - Cineno28
@Darin Dimitrov,foreach (string item in values) 可以实现吗? - Jeyhun

0

你走在正确的道路上,我自己也做过你正在寻找的事情。然而,与其只是将一个字符串传递给你的函数,我会传递一个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 );
}

0

你尝试做的事情在逻辑上是完全合理的,我可以理解为什么你会采用这种实现方式。然而,你所尝试的做法非常危险,作为一个新手使用ASP.NET,你可能不知道还有其他各种选项可供选择,这些选项可以使你更轻松、更安全地管理数据。

@iamkrillin 暗示了一种这样的技术——对象关系映射(ORM)。.NET框架实际上支持一种名为Entity Framework的ORM。我相信他建议你研究ORM的原因是因为你的设计实际上与ORM的工作原理非常相似。它们是抽象类,代表数据库中的表格,可以很容易地使用LINQ进行查询。LINQ查询自动参数化,并且无需担心查询安全性的问题。它们可以动态生成SQL(就像你将字符串传递给数据访问类时一样),并且在返回数据的方式上更加灵活(数组、列表等等)。

然而,ORM的一个缺点是它们有相当陡峭的学习曲线。一个更简单的选择(虽然比EF旧一些)是使用Typed Datasets。与建立ORM相比,Typed Datasets要容易得多,并且通常更容易实现。虽然不像ORM那样灵活,但它们以一种简单、安全和已经解决的方式完成了你想要做的事情。幸运的是,当ASP.NET首次推出时,培训视频重点关注了Typed Datasets,因此有各种高质量的免费可用的视频/教程可以帮助您快速上手。


2
如果你不想使用像Entity Framework这样的烂ORM,那就随便选吧,有很多选择。NHibernate、LightSpeed,还有数十亿个微型ORM,比如PetaPoco、Massive、ServiceStack.OrmLite、Dapper... - Phill
@DarinDimitrov 为什么这样不好?你能提供一些资源、文章、测试等来支持你的论点吗? - Subliminal Hash
1
@Emin,当然,看看StackOverflow上关于人们在使用Entity Framework时遇到问题的问题数量。现在再看看StackOverflow上关于人们在使用其他ORM时遇到问题的问题数量。然后计算一下。数字越大,情况越糟糕,相信我,EF在这场比赛中领先得多 :-) 但是我甚至不明白为什么我们在这里争论ORM。OP有一个特定的问题,与ORM无关。这是关于如何使用ADO.NET避免SQL注入攻击的问题。 - Darin Dimitrov
@DarinDimitrov,我相信你。最近我开始在一个简单的项目中使用它,并在思考将来在更复杂的项目中可能会遇到哪些问题。既然你反对EF,我想你可能可以给我一些相关信息,所以我才问的。 - Subliminal Hash
1
@ajax81 这真是太有帮助了。非常感谢您提供的进一步信息链接。我一定会深入研究这些内容的。 - Cineno28
显示剩余2条评论

-3
我的建议是使用ORM。现在有很多选择。

3
为什么在 ADO.NET 已经有防止 SQL 注入所需的一切时还要使用 ORM? 使用 ORM 有时可能会增加开销。 - Darin Dimitrov
@darindimitrov 我并不是在争论这个问题,只是指出他正在做的正是ORM解决的问题。当然,可能会有性能损失(取决于ORM),但与SQL注入可能造成的损害相比,这些都微不足道 - 更不用说所有的生产力收益等等了。最后,由于OP提到他是“新手”,我选择建议一个更适合初学者的选项。 - iamkrillin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接