转义数据库对象名

时间:2017-08-22 09:40:04

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

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

方式去逃避名称的方式:

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

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

2 个答案:

答案 0 :(得分:2)

您可以使用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?

答案 1 :(得分:1)

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

  

在正确的目录案例中给出一个不带引号的标识符,返回该标识符的正确引用形式。这包括正确转义标识符中的任何嵌入式引号。

https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommandbuilder.quoteidentifier.aspx