sql-server – 在不使用QUOTENAME的情况下,在SQL Server中正确转义分隔标识符

前端之家收集整理的这篇文章主要介绍了sql-server – 在不使用QUOTENAME的情况下,在SQL Server中正确转义分隔标识符前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
代码必须做些什么来清理标识符(表,视图,列),而不是用双引号将它们包装起来,并在标识符名称中出现“double up”双引号?参考文献将不胜感激.

我继承了一个具有自定义对象关系映射(ORM)系统的代码库. sql无法在应用程序中编写,但ORM仍必须最终生成要发送到sql Server的sql.所有标识符都用双引号引用.

string QuoteName(string identifier) 
{ 
    return "\"" + identifier.Replace("\"","\"\"") + "\"";
}

如果我在sql中构建这个动态sql,我会使用内置的sql Server QUOTENAME函数

declare @identifier nvarchar(128);
set @identifier = N'Client"; DROP TABLE [dbo].Client; --';

declare @delimitedIdentifier nvarchar(258);
set @delimitedIdentifier = QUOTENAME(@identifier,'"');

print @delimitedIdentifier;
-- "Client""; DROP TABLE [dbo].Client; --"

我没有找到任何关于如何在sql Server中转义带引号的标识符的权威文档.我找到了Delimited Identifiers (Database Engine),我也看到了this stackoverflow question关于消毒.

如果必须调用QUOTENAME函数只是为了引用那些不需要sql Server的大量流量的标识符.

关于sql注入,ORM似乎已经过深思熟虑了.它位于C#中,早于nHibernate端口和实体框架等.所有用户输入都是使用ADO.NET sqlParameter对象发送的,它只是我在这个问题中关注的标识符名称.这需要适用于sql Server 2005和2008.

2010-03-31更新

虽然应用程序不允许在查询中允许用户输入标识符名称,但ORM通过它对ORM样式读取和自定义查询具有的查询语法来执行.正是我试图最终阻止所有可能的sql注入攻击的ORM,因为它非常小并且易于验证而不是所有应用程序代码.

查询界面的一个简单示例:

session.Query(new TableReference("Client")
    .Restrict(new FieldReference("city") == "Springfield")
    .DropAllBut(new FieldReference("first_name"));

ADO.NET发送此查询

exec sp_executesql N'SELECT "T1"."first_name" 
FROM "dbo"."Client" AS "T1" 
WHERE "T1"."city" = @p1;',N'@p1 nvarchar(30)',N'Springfield';

也许有必要考虑一下nHibernate查询语言(HQL)中的类似内容

using (ISession session = NHibernateHelper.OpenSession())
{
    Client client = session
        .CreateCriteria(typeof(Client))  \\ <-- TableReference in example above
        .Add(Restrictions.Eq("city","Springfield"))  \\ <-- FieldReference above
        .UniqueResult<Client>();
    return client;
}

也许我应该看看nHibernate如何保护输入.

解决方法

您的QuoteName函数需要检查长度,因为T-sql QUOTENAME函数指定它返回的最大长度.使用你的例子:
String.Format(@"declare @delimitedIdentifier nvarchar(258);
set @delimitedIdentifier = {0};",QuoteName(identifier));

如果QuoteName(标识符)超过258个字符,则在分配给@delimitedIdentifier时将以静默方式截断它.当发生这种情况时,您可能会打开@delimitedIdentifier不正确转义的可能性.

微软的“安全软件开发人员”Bala Neerumalla有an MSDN article,它更深入地解释了这个主题.本文还包含了我发现的“关于如何在sql Server中转义引用标识符的权威文档”中最接近的内容

The escaping mechanism is simply doubling up the occurrences of right square brackets. You don’t need to do anything with other characters,including left square brackets.

这是我目前使用的C#代码

/// <summary>
/// Returns a string with the delimiters added to make the input string
/// a valid sql Server delimited identifier. Brackets are used as the
/// delimiter. Unlike the T-sql version,an ArgumentException is thrown
/// instead of returning a null for invalid arguments.
/// </summary>
/// <param name="name">sysname,limited to 128 characters.</param>
/// <returns>An escaped identifier,no longer than 258 characters.</returns>
public static string QuoteName(string name) { return QuoteName(name,'['); }

/// <summary>
/// Returns a string with the delimiters added to make the input string
/// a valid sql Server delimited identifier. Unlike the T-sql version,/// an ArgumentException is thrown instead of returning a null for
/// invalid arguments.
/// </summary>
/// <param name="name">sysname,limited to 128 characters.</param>
/// <param name="quoteCharacter">Can be a single quotation mark ( ' ),a
/// left or right bracket ( [] ),or a double quotation mark ( " ).</param>
/// <returns>An escaped identifier,no longer than 258 characters.</returns>
public static string QuoteName(string name,char quoteCharacter) {
    name = name ?? String.Empty;
    const int sysnameLength = 128;
    if (name.Length > sysnameLength) {
        throw new ArgumentException(String.Format(
            "name is longer than {0} characters",sysnameLength));
    }
    switch (quoteCharacter) {
        case '\'':
            return String.Format("'{0}'",name.Replace("'","''"));
        case '"':
            return String.Format("\"{0}\"",name.Replace("\"","\"\""));
        case '[':
        case ']':
            return String.Format("[{0}]",name.Replace("]","]]"));
        default:
            throw new ArgumentException(
                "quoteCharacter must be one of: ',\",[,or ]");
    }
}

猜你在找的MsSQL相关文章