【问题标题】:Using C# SQL Parameterization on Column Names对列名使用 C# SQL 参数化
【发布时间】:2013-03-03 10:46:11
【问题描述】:

我遇到了问题。我想让它工作,但它没有:

SqlDataSource.SelectCommand = "SELECT blah1, blah2 FROM myTable WHERE @ColumnName = @Value";

SqlDataSource.SelectParameters.Add("ColumnName", System.Data.DbType.String, "one");
SqlDataSource.SelectParameters.Add("Value", System.Data.DbType.String, "two");

它不会替换第一个参数“ColumnName”。如果我删除该参数并像这样将列名放入其中,它将起作用:

SqlDataSource.SelectCommand = "SELECT blah1, blah2 FROM myTable WHERE one = @Value";

SqlDataSource.SelectParameters.Add("Value", System.Data.DbType.String, "two");

我有一个用户界面,用户可以在其中选择要搜索的数据库列名。我想保护自己免受任何形式的注射攻击。有什么想法可以让我完成这项工作吗?

我读到的一个想法是使用查找表从DropDownList 获取索引并以这种方式提取列名。我可以做到这一点,但如果可能的话,我宁愿让参数化工作,因为这对我来说似乎更自然。

提前感谢您提供的任何帮助。

【问题讨论】:

  • 这根本不被支持。您需要手动构建 SQL 文本,检查以确保列名有效(可能使用enum)..
  • 如果这个方法有效,那么参数化查询就变得不安全了。

标签: asp.net sql sqldatasource


【解决方案1】:

我想出了一种方法来解决参数化列名的问题。我遇到了同样的问题,但想出了一个不同的方法,因为我是唯一使用列名的人,所以我相信这仍然是一个安全的选择。

            String sqlcomm = "SELECT * FROM Asset WHERE " + assetColName + " = ";
            command.CommandText = sqlcomm + "$assetColValue";

            //command.CommandText = @"SELECT * FROM Asset WHERE $assetColName = '$assetColValue'";
            //command.Parameters.AddWithValue("$assetColName", assetColName);

            command.Parameters.AddWithValue("$assetColValue", assetColValue);

从上面的代码可以看出。我几乎尝试了你所做的,然后我不得不发表评论。然后我将字符串连接在一起,并能够使用我的参数化列名和值,然后安全地添加值。然而,列名是不安全的,但这是一种只有我会使用的方法,所以它仍然有点安全。如果您想更安全,可以添加正则表达式,但您知道修复的想法。

【讨论】:

    【解决方案2】:

    恐怕你做不到,你可以做的是一个小技巧:

    SELECT blah1, blah1 FROM myTable 
    WHERE (@blah1 is null or blah1 = @blah1)
       or (@blah2 is null or blah2 = @blah2)
    

    并提供所有参数@blah1、@blah2,但只分配您需要的参数。

    Mike Christensen 为您提供的NB 解决方案基本上是在正确的条件下构建字符串,最简单的情况是

    public bool BuildQueryWithCondition(string fieldName, string fieldValue) {
       var queryTemplate = "SELECT blah1, blah1 FROM myTable WHERE {0} = @Value"
         , query = string.Format(queryTemplate, fieldName)
       SqlDataSource.SelectCommand = query;
       SqlDataSource.SelectParameters.Add("Value", System.Data.DbType.String, fieldValue);
    }
    

    【讨论】:

    • 我有点好奇这是否会产生任何重大的性能影响或产生非最佳执行计划。
    • 好吧,您可能可以在列出的任何条件下执行查询,但事实并非如此。此外,我怀疑准备一个这样的语句比只指定一个条件的执行未准备的语句要好。但是你的好奇心是完全健康的,继续做一些测试吧!
    • 无论哪种方式,SQL 解析和编译都会非常快。我担心它可能会触及一堆不必要的索引,或者在内存中构建巨大的位图索引或其他东西。但是,是的,所有的猜测,我必须首先对所涉及的数据类型、数据库的大小以及问题中未指定的各种其他变量有一个很好的了解。
    • SQL 被认为是在水晶和 SSRS 等报告逻辑中为用户提供“全部”价值的最佳实践,我建议不要过多地推测它,尤其是。如果您无法辨别它为什么会或不会出现性能问题。
    • 是的,我们实际上完全符合您刚才描述的情况。但这不是这种方法的问题,我们只是在一张表上有适当的索引。
    【解决方案3】:

    由于查询参数是在解析 SQL 并生成执行计划之后解析的,因此您实际上无法使用参数动态构建 SQL。我建议以安全的方式构建 SQL 字符串本身。也许首先创建一个有效列名的enum

    enum DbColumns { One, Two, Three };
    

    然后像这样构建 SQL 字符串:

    DbColumns colName = (DbColumns)Enum.Parse(typeof(DbColumns), "One");
    SqlDataSource.SelectCommand = String.Format("SELECT blah1, blah1 FROM myTable WHERE {0} = @Value", colName);
    

    另一个想法是使用正则表达式验证列名,也许只允许[a-z]

    【讨论】:

    • 哦,在我更新我的那一刻,你也给出了答案=)
    • 呵呵,没关系。这两个答案都值得考虑。
    • 我想出的解决方案基本上是相同的——使用下拉列表的选定索引作为对最初绑定到下拉列表的列名的查找。如果索引不在界限内或类型不正确(也就是无效),我不返回任何结果。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-11
    • 1970-01-01
    • 2011-09-30
    • 2021-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多