用MySQL表名作为参数

6
我正在尝试设置表名作为参数传递到命令文本中,但我没能使其起作用。我查看了一些问题,如:使用C#的MySQL参数查询,但我没有成功。

这是相关的代码(connection == 包含连接字符串的MySqlConnection):

public static DataSet getData(string table)
{
    DataSet returnValue = new DataSet();
    try
    {
        MySqlCommand cmd = connection.CreateCommand();
        cmd.Parameters.AddWithValue("@param1", table);
        cmd.CommandText = "SELECT * FROM @param1";

        connection.Open();

        MySqlDataAdapter adap = new MySqlDataAdapter(cmd);
        adap.Fill(returnValue);
    }
    catch (Exception)
    {   
    }
    finally
    {
        if (connection.State == ConnectionState.Open)
            connection.Close();
    }
    return returnValue;
}

如果我更改:

cmd.CommandText = "SELECT * FROM @param1";

to:

cmd.CommandText = "SELECT * FROM " + table;

作为一种测试方式,这个方法是可行的(我将xml从数据集写入控制台进行检查)。所以我相当确定问题只是使用参数功能的方式不对。有什么建议吗?
此外,请纠正我如果我错了,但是使用参数功能应该完全防止SQL注入,对吗?
2个回答

9
你不能参数化表名、列名或任何其他数据库对象。你只能参数化你的值。
在你的SQL查询中,你需要将它作为字符串连接传递,但在这之前,我建议使用强大的验证或白名单(只有一组固定的可能正确的值)。
引用:
如果你用“参数功能”指的是参数化语句,那么是的,这是正确的。
顺便说一下,要注意,有一个概念叫做动态SQL,支持SELECT * FROM @tablename,但不建议使用。
正如我们所看到的,我们可以通过动态SQL来实现此过程,但显然,如果在存储过程中生成动态SQL,我们不会获得任何优势。您可以从客户端发送动态SQL。因此,好的:1)如果SQL语句非常复杂,则可以节省一些网络流量并进行封装。2)正如我们所看到的,从SQL 2005开始有处理权限的方法。然而,这是一个坏主意。
似乎有几个原因使人们想要参数化表名。一些人似乎是新手SQL编程的人,但具有C ++,VB等其他语言的经验,其中参数化是一件好事。将表名参数化以实现通用代码并增加可维护性似乎是好的程序员美德。
但是当涉及到数据库对象时,旧的真理并不适用。在正确的数据库设计中,每个表都是唯一的,因为它描述了一个独特的实体。(或者至少应该是!)当然,最终可能会有十多个查找表,它们都有一个ID,一个名称列和一些审计列。但是它们确实描述了不同的实体,并且它们的相似之处应被视为偶然,并且未来的需求可能会使表格更加不同。

啊,我明白了。谢谢你的建议。计时器结束后我会接受它。对了,我的意思是MySqlCommand.Parameters的功能(因此Parameters大写,如果不够清楚,抱歉)。 - user2875994
@user2875994 很高兴能帮忙。添加了一些更多的信息。 - Soner Gönül
有用的信息!我认为我可能会使用下面提到的sqlcommandbuilder,因为表变量永远不会由用户输入设置(它在静态类中的私有方法中使用。我知道现在写成public但那只是闲逛)。这个类只需要获取很多表格,所以最好不要每次添加新表格时都更新白名单。正如我所提到的,它并不是由用户输入设置的,所以安全性在这方面并不是一个问题(但仍然很好阻止注入,因为谁知道呢?)。 - user2875994

5

将表名作为参数使用是不正确的。在SQL中,参数仅适用于值,而不是列或表的标识符。

一个选择是使用SqlCommandBuilder类,它将转义您的表名并且不易受SQL注入攻击:

SqlCommandBuilder cmdBuilder = new SqlCommandBuilder();
string tbName = cmdBuilder.QuoteIdentifier(tableName);

现在您可以在语句中使用tbName,因为它已经不易受SQL注入攻击。


谢谢您的回复! - user2875994
@user2875994...不用谢。请查看我的更新答案。 - Salah Akbari
这真的非常有用!谢谢! - user2875994
在尝试过后的补充说明:当你运行你粘贴的那段sqlcommandbuilder代码时,“tbname”字符串被包含在字符串内的''中。因此,在你上面的例子中,如果tableName是generictable1,则tbName是'generictable1'。然而,它仍然可以找到表,但是如果你想要测试一个包含允许的表的白名单,这会使得测试变得有点奇怪,除非你将''添加到白名单中,或者在测试白名单之前从tbName字符串中删除它们,或者你测试tableName变量是否在白名单中,然后使用tbName。 - user2875994
1
我知道这个答案已经存在一段时间了,但它引用了sqlcommandbuilder类,而该问题是关于mysql的,因此答案应该引用mysqlcommandbuilder类。请参见https://dev.mysql.com/doc/dev/connector-net/8.0/html/T_MySql_Data_MySqlClient_MySqlCommandBuilder.htm。 - Shadow

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