【问题标题】:i'm lost: what is wrong with this ado.net code?我迷路了:这个 ado.net 代码有什么问题?
【发布时间】:2025-06-04 23:20:01
【问题描述】:

好吧,我希望问题很清楚,代码是这样的:

string sql = "delete from @tabelnaam";
            SqlCommand sc = new SqlCommand();

                sc.Connection = getConnection();
                sc.CommandType = CommandType.Text;
                sc.CommandText = sql;
                SqlParameter param = new SqlParameter();

                param.Direction = ParameterDirection.Input;
                param.ParameterName = "@tabelnaam";
                param.Value  = tableName;

                sc.Parameters.Add(param);
                OpenConnection(sc);
                sc.ExecuteScalar();

tableName 被提供给这个函数。

我得到了例外:

Must declare the table variable @tabelnaam

【问题讨论】:

    标签: c# ado.net


    【解决方案1】:

    IIRC,你不能用表名代替参数。

    而是构建包含正确表名的 SQL 字符串。

    【讨论】:

    • 不会导致sql注入吗?
    • @Michel:你是怎么得出这个结论的?
    • 当然,如果字符串的来源来自不受信任的来源(例如用户输入),则存在 sql 注入风险。如果我输入以下“表名”会怎样:“MyTable; DROP DATABASE MyDb”。
    【解决方案2】:

    做出改变

    而不是使用参数使用这个

    string sql = string.format( "delete from {0}",tableName);
    

    使用 executenonquery 而不是 ExecuteScalar

    sc.ExecuteNonQuery();
    

    【讨论】:

    • 不会导致sql注入吗?
    • 您可以通过正确验证表名来避免这种情况
    【解决方案3】:

    正如其他人所说,您不能参数化表名。

    但是,正如您在 cmets 的其他答案中正确提到的那样,使用简单的字符串操作可能会引入 SQL 注入风险:

    如果您的表名输入来自不受信任的来源,例如用户输入,则使用此:

    string sql = string.format( "DELETE FROM {0}",tableName);
    

    让您打开正在插入的表名“myTable; DROP DATABASE MyDb”,给您:

    DELETE FROM myDb; DROP DATABASE MyDB
    

    解决这个问题的方法是对表名进行分隔,如下所示:

    string sql = string.format("DELETE FROM dbo.[{0}]", tableName);
    

    结合检查输入不包含'['或']';您可能应该检查它是否不包含任何其他不能用作表名的字符,例如句点和引号。

    【讨论】:

    • 你的意思是dbo. 不是do.?不过,表可以存在于其他模式中。
    • @Rup - 我做到了 - 好地方,谢谢。的确,他们可以生活在其他模式中。我只是想在 OP 上留下这样的想法,以使事情尽可能地受到限制。如果表在其他模式中,则需要放松,但仅在是这种情况下。
    【解决方案4】:

    我不认为你可以参数化表名。根据我的阅读,您可以通过动态 sql 并调用 sp_ExecuteSQL 来完成。

    【讨论】:

    • 嗯?所以我可以这样做'从名称 = @name 的客户端中选择 *',但我不能这样做:'从 @table 中删除'?
    【解决方案5】:

    您的 SQL 不正确,您正在从表变量中删除但尚未定义该变量。

    更新:正如有人指出的那样,您正在尝试动态构建查询字符串,但无意中使用了 SQL 参数(这些参数不能用作字符串文字的占位符)。

    【讨论】:

    • 我不确定我明白你的意思。
    • 您使用的@tableName 语法后跟comm.Parameters 集合为您提供了一种称为“参数化SQL”的东西。该命令试图将“值”放入 @tableName 而不是您想要的字符串文字。您只是想使用字符串构建一个没有任何参数的 SQL 块。
    【解决方案6】:

    【讨论】:

      【解决方案7】:

      你不能参数化表名,你必须将它注入到命令文本中。

      您可以并且应该做的是通过这样分隔名称来保护自己免受 SQL 注入:

      public static string Delimit(string name) {
          return "[" + name.Replace("]", "]]") + "]";
      }
      
      // Construct the command...
      sc.CommandType = CommandType.Text;
      sc.CommandText = "delete from " + Delimit(tableName);
      sc.ExecuteNonQuery();
      

      有关更多背景信息,请参阅 herehere

      【讨论】: