【问题标题】:parameterized queries vs. SQL injection参数化查询与 SQL 注入
【发布时间】:2013-07-07 04:06:20
【问题描述】:

我是 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 查询。所以它会比内联查询运行得更快。只是建议使用存储过程。
  • 去过一次,我强烈你钻研学习和ORM(我选择了实体框架)。我花了很多时间编写这样的代码才发现这是多么不必要!在为时已晚之前,学习实体框架。如果你没有太复杂的查询,主要是简单的 CRUD 操作,学习起来并不难..
  • Hiren,存储过程不是预先执行的查询。你是说执行计划吗?
  • @HirenDhaduk 存储过程已经过时了!如果可以避免,请不要使用它们。
  • @DarinDimitrov 我们并不是建议使用 ORM 来解决注入攻击的问题。我们建议的是一种不同的编程方法,他不需要处理这些事情。

标签: c# asp.net


【解决方案1】:

这可行,但仍然容易受到注射,对吧?

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

【讨论】:

  • 感谢您的帮助。每个人都提供了很多有用的信息,但我想我会将这个标记为答案,因为它与我最初的目标直接相关。但是,我将研究所有建议以了解有关此过程的更多信息。谢谢!
  • @Darin Dimitrov , foreach (string item in values) 有可能吗?
【解决方案2】:

您尝试做的事情完全合乎逻辑,我可以理解您为什么会实现此实施。但是,您尝试做的事情非常危险,而且作为 ASP.NET 的新手,您可能不知道还有许多其他选项可供您使用,这些选项可以让您更轻松、更安全地管理数据。

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

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

【讨论】:

  • 如果你不想使用像实体框架这样糟糕的 ORM。随你挑,有很多。 NHibernate,光速。以及数十亿个微型 ORM,例如 PetaPoco、Massive、ServiceStack.OrmLite、Dapper...
  • @DarinDimitrov 为什么不好?你能用任何资源/文章/测试/等来支持你的论点吗??
  • @Emin,当然,看看 StackOverflow 上关于实体框架问题的人的问题数量。现在看看 StackOverflow 上关于人们对其他 ORM 有问题的问题的数量。然后数一数。数字越大,越差,相信我,EF 在这场竞争中处于领先地位 :-) 但我什至不明白为什么我们在这里争论 ORM。 OP 有一个与 ORM 无关的特定问题。这是关于如何使用 ADO.NET 避免 SQL 注入攻击。
  • @DarinDimitrov 我相信你。我最近开始将它用于一个简单的项目,并且正在考虑将来使用更高级的项目可能会遇到什么问题。既然你反对 EF,我想你可以给我一些关于它的信息,这就是我问的原因。
  • @ajax81 这是一个很大的帮助。也非常感谢您提供更多信息的链接。我一定会进一步研究这一切。
【解决方案3】:

你在正确的轨道上,实际上我也做了你正在寻找的事情。但是,我不只是将字符串传递给您的函数,而是传递了一个 SQL Command 对象...因此,您可以正确构建所有命令和参数,然后说...在这里,运行它,它准备好了。类似的东西

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 );
}

【讨论】:

    【解决方案4】:

    我的建议:使用 orm。从现在开始有很多选择

    【讨论】:

    • 当 ADO.NET 已经具备防止 SQL 注入所需的一切时,为什么还要使用 ORM? ORM 有时可能是开销。
    • @darindimitrov 我不是在争论这一点,只是指出他正在做的正是 ORM 解决的问题。当然可能会有性能损失(取决于 ORM),但与 SQL 注入可能造成的损害相比,这算不了什么 - 没关系所有的生产力提升等等。最后,由于 OP 提到他是“新人”,我选择建议一个对刚入门的人更友好的选项
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-01
    • 1970-01-01
    • 2021-11-16
    • 2014-10-14
    • 1970-01-01
    • 2012-06-09
    • 1970-01-01
    相关资源
    最近更新 更多