转义数据库对象名称

Escape database object names

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

转义名称的方法是什么:

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

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

您可以使用 QUOTENAME SQL 服务器内置函数来实现此目的。

因为它看起来很像您正在用 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 命名空间中的 classes 来展示这一点,这里有一个 class 执行此行为,当给定一个连接字符串用于交谈时至 SQL 服务器:

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?

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

Given an unquoted identifier in the correct catalog case, returns the correct quoted form of that identifier. This includes correctly escaping any embedded quotes in the identifier.

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