Правильное экранирование идентификаторов с разделителями в SQL Server без использования QUOTENAME - PullRequest
8 голосов
/ 30 марта 2010

Есть ли что-то еще, что код должен делать для очистки идентификаторов (таблица, представление, столбец), кроме как заключать их в двойные кавычки и "удваивать" двойные кавычки, присутствующие в имени идентификатора? Ссылки будут оценены.

Я унаследовал кодовую базу с системой пользовательского объектно-реляционного отображения (ORM). SQL не может быть записан в приложении, но ORM должен в конечном итоге генерировать SQL для отправки на SQL Server. Все идентификаторы заключены в двойные кавычки.

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. Я нашел Идентификаторы с разделителями (компонент Database Engine) и также увидел этот вопрос о потоке стека о очистке.

Если бы пришлось вызывать функцию QUOTENAME, чтобы просто заключить в кавычки идентификаторы, которые являются большим трафиком для SQL Server, который не требуется.

ORM, кажется, довольно хорошо продуман в отношении SQL-инъекций. Он находится на C # и предшествует порту nHibernate, Entity Framework и т. Д. Весь пользовательский ввод отправляется с использованием объектов ADO.NET SqlParameter, это просто имена идентификаторов, которые меня беспокоят в этом вопросе. Это должно работать на SQL Server 2005 и 2008.


Обновление от 2010-03-31

Хотя приложение не должно разрешать ввод пользователя для имен идентификаторов в запросах, ORM делает это с помощью синтаксиса запроса, который используется как для чтения в стиле ORM, так и для пользовательских запросов. Это ORM, который я пытаюсь в конечном итоге предотвратить всеми возможными атаками SQL-инъекций, поскольку это очень мало и легко проверить в отличие от всего кода приложения.

Простой пример интерфейса запроса:

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 Query Language (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 защищает ввод.

Ответы [ 2 ]

19 голосов
/ 18 августа 2010

Ваша QuoteName функция должна проверить длину, потому что функция T-SQL QUOTENAME определяет максимальную длину, которую она возвращает. Используя ваш пример:

String.Format(@"declare @delimitedIdentifier nvarchar(258);
set @delimitedIdentifier = {0};", QuoteName(identifier));

Если QuoteName(identifier) длиннее 258 символов, оно будет беззвучно обрезано при назначении на @delimitedIdentifier. Когда это происходит, вы открываете возможность неправильного экранирования @delimitedIdentifier.

Существует статья MSDN Бала Нерумалла, «разработчика программного обеспечения для безопасности в Microsoft», в которой более подробно объясняется тема. В статье также содержится самая близкая вещь, которую я обнаружил к «исчерпывающей документации о том, как избежать кавычек идентификаторов в SQL Server»:

Механизм побега просто удваивает вхождения в правые квадратные скобки. Вам не нужно ничего делать с другими персонажами, включая левые квадратные скобки.

Это код 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 ]");
    }
}
0 голосов
/ 30 марта 2010

Разве вы не можете просто использовать [и] разделители вместо кавычек (одинарные или двойные)?

Идентификаторы никогда не должны содержать кавычек (если вам не повезло больше, чем сейчас), поэтому вы удаляете обычный коэффициент использования кавычек в именах и т. Д.

Edit:

Но если вызовы ORM уже параметризованы, вам не нужно об этом беспокоиться, нет? Использование [и] устраняет необходимость сложного экранирования в строках c #

...