【问题标题】:Escape database object names转义数据库对象名称
【发布时间】:2017-08-22 09:40:03
【问题描述】:

我需要在 C# 中使用字符串连接构建 SQL Server 2016 脚本(该脚本将通过 ADO.NET 执行)。我不能使用通常会使用的查询参数,因为该脚本更像是一个设置脚本并且包含不可参数化的语句。

转义名称的方法是什么:

"ALTER DATABASE " + Escape(databaseName) + " ADD ..."

不容易受到 SQL 注入的影响?如何实现Escape?目前我在所有名称周围都使用方括号,但是,这当然是不够的......

【问题讨论】:

  • 您可以验证检查某些赠品的变量,例如 [ ] ;和“等
  • @BugFinder,虽然丑陋、错误、糟糕,而且 [在此处插入更多形容词],但在 SQL Server 中有一个名为 [ 的表是完全合法 =(
  • true 但您可以将其作为您的应用程序的要求:) 您不这样做:D
  • 也许可以阻止此类名称 - 需要与我的产品负责人核实 - 是否有允许 SQL 注入的“特殊字符”的完整列表?
  • @D.R.,如果你真的走黑名单/白名单的路线,请去白名单。忘记将允许的字符添加到白名单比忘记将不允许的字符添加到黑名单更安全 =)

标签: c# sql-server ado.net sql-server-2016


【解决方案1】:

您可以使用QUOTENAME SQL Server 内置函数来实现这一点。

因为它看起来非常像您在 C# 中构建脚本,在 SQL 中执行它之前,您可能希望有一个 C# 函数,它可以访问数据库来执行此操作,也许还将结果值存储在 Dictionary<string, string> 中,以消除已被引用的字符串的往返。

例如:

private Dictionary<string, string> _quotedNames = new Dictionary<string, string>();

private string GetSqlQuotedName(string name)
{
    if (!_quotedNames.ContainsKey(name))
    {
        _quotedNames[name] = GetSqlQuotedNameFromSqlServer(name);
    }

    return _quotedNames[name];
}

private string GetSqlQuotedNameFromSqlServer(string name)
{
    /// Code here to use your Data access method of choice to basically execute
    /// SELECT QUOTENAME(name) and return it
}

事实上,只是为了使用 System.Data.SqlClient 命名空间中的类来展示这一点,当给定用于与 SQL Server 对话的连接字符串时,这里有一个执行此行为的类:

public class SqlNameEscaper
{
    private Dictionary<string, string> _quotedNames = new Dictionary<string, string>();
    private string _connectionString = string.Empty;

    public SqlNameEscaper(string connectionString)
    {
        _connectionString = connectionString;
    }

    public string GetSqlQuotedName(string name)
    {
        if (!_quotedNames.ContainsKey(name))
        {
            _quotedNames[name] = GetSqlQuotedNameFromSqlServer(name);
        }

        return _quotedNames[name];
    }

    private string GetSqlQuotedNameFromSqlServer(string name)
    {
        using (var connection = new SqlConnection(_connectionString))
        {
            connection.Open();
            using (var command = new SqlCommand("SELECT QUOTENAME(@name)", connection))
            {
                command.Parameters.AddWithValue("@name", name);
                var result = command.ExecuteScalar();

                return result.ToString();
            }
        }
    }
}

然后可以通过这样做来调用它:

var sne = new SqlNameEscaper(@"CONNECTION_STRING_HERE");
var bracket = sne.GetSqlQuotedName("[");

或者,在您的示例中:

var sqlNameEscaper = new SqlNameEscaper(@"CONNECTION_STRING_HERE");
var text = "ALTER DATABASE " + sqlNameEscaper.GetSqlQuotedName(databaseName) + " ADD ...";

dba.stackexchange.com 上还有一个问题值得一读:Should we still be using QUOTENAME to protect from injection attacks?

【讨论】:

  • QUOTENAME 是否也可用于“参数”,例如 ADD FILE (name = ' &lt;in here&gt;', ...)
  • @D.R. - 可能,尽管您更有可能开始达到文档提到的 128 个字符的限制。如果您使用的是我上面提出的包装函数,那么生成的字符串可以在您想要的任何地方使用! =)
【解决方案2】:

如果您使用的是常规 .NET,请使用 SqlCommandBuilder.QuoteIdentifier:

在正确的目录大小写中给定一个未引用的标识符,返回该标识符的正确引用形式。这包括正确转义标识符中的任何嵌入引号。

https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommandbuilder.quoteidentifier

【讨论】:

    猜你喜欢
    • 2015-07-28
    • 2014-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多