在不注入 SQL 的情况下将数据库名称传递给 SQL 查询

Passing database name to SQL query without SQL injection

提问人:jen 提问时间:10/27/2022 最后编辑:Mark Rotteveeljen 更新时间:10/27/2022 访问量:220

问:

我需要在不注入 SQL 的情况下将数据库名称传递给我的 SQL 查询。

我首先获取所有数据库的列表,并检查我传递的数据库名称是否在列表中。如果是,我将转义我的数据库名称并将其传递给我的 SQL 查询。这是防止SQL注入的正确方法,因为我无法参数化数据库名称。另外,我应该如何处理异常?ArgumentException 可以吗?

public async Task<int> GetTableId(string database)
{
    int res;

    using (IDbConnection db = new SqlConnection(_settings.SqlServerConnString))
    {
        try
        {
            string sqlDatabases = $"SELECT [name] FROM [sys].[databases]";
            List<string> resDatabases = db.Query<string>(sqlDatabases).ToList();
            if (!resultDbList.Contains(database))
                throw new ArgumentException(nameof(database));

            var builder = new SqlCommandBuilder();
            string sanitizedDatabase = builder.QuoteIdentifier(database);

            string sqlId = $"SELECT DISTINCT [Id] FROM {sanitizedDatabase}.[dbo].[Table]";
            res = db.Query<int>(sqlId).FirstOrDefault();
        }
        catch (Exception e)
        {
            _logger.LogError(e, "Error querying table id from database", new { database });
            throw;
        }
    }

    return result;
}
C# ASP.NET Core 异常 SQL 注入

评论

0赞 Panagiotis Kanavos 10/27/2022
参数用于传递数据,就像函数中的参数一样。数据库名称不是数据。它更接近程序集的名称
0赞 jen 10/27/2022
@PanagiotisKanavos 我知道数据库名称不是数据,并且我不能像使用这样的列值那样将 SqlParameter 用于数据库名称。但是,在这种情况下,数据库名称是动态的,我只想确认这是否是防止 SQL 注入的最佳方法。Id = @Id
0赞 Panagiotis Kanavos 10/31/2022
如果你明白它不是名称,你应该明白,如果不构造一个字符串命令,你就无法传递这个名称,即使这是一个 .可以使用参数在连接字符串中指定数据库名称。但是,切换到另一个数据库需要显式命令USE someDB;Initial Catalog=myDB

答:

1赞 Marc Gravell 10/27/2022 #1

无法对数据库进行参数化,因此留下的选项有限:

  • 串联;根据您的示例,尽管建议使用它来处理空格等(尽管您还想检查![your db name here].whatever]
  • 通过语句(实际上并不比第一个选项更好,而且可移植性较差 - 而且它仍然需要是文字,而不是参数)USE dbname;
  • 在连接字符串中指定数据库,或通过用户的默认数据库指定数据库

不断将文本数据库名称连接到每个查询中将导致大量的字符串分配改动,因此,如果可以在连接级别(通过连接字符串)执行此操作,则效率可能会高得多。