Есть ли что-либо еще, что код должен сделать для очистки идентификаторов (таблица, представление, столбец) кроме перенести их в двойные кавычки и "согнуть" двойные кавычки, существующие на имя идентификатора? Ссылки ценились бы.
Я наследовал кодовую базу, которая имеет реляционное пользовательским объектом отображение (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. Я нашел Выделенные идентификаторы (Механизм базы данных), и я также видел этот stackoverflow вопрос об очистке.
Если этому придется вызвать функцию QUOTENAME только для заключения в кавычки идентификаторов, который является большим трафиком к SQL Server, который не должен быть необходим.
ORM, кажется, вполне прилично продуман относительно Внедрения SQL. Это находится в C# и предшествует nHibernate порту и Платформе Объекта и т.д. Весь ввод данных пользователем отправляется с помощью объектов ADO.NET SqlParameter, это - просто имена идентификатора, которыми я обеспокоен в этом вопросе. Это должно работать над SQL Server 2005 и 2008.
Обновление 31.03.2010
В то время как приложение, как предполагается, не позволяет ввод данных пользователем для имен идентификатора в запросах, 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 Языке запросов (HQL):
using (ISession session = NHibernateHelper.OpenSession())
{
Client client = session
.CreateCriteria(typeof(Client)) \\ <-- TableReference in example above
.Add(Restrictions.Eq("city", "Springfield")) \\ <-- FieldReference above
.UniqueResult();
return client;
}
Возможно, я должен посмотреть и видеть, как nHibernate защищает вход.
Ваша функция 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 ]");
}
}
Можно ли просто использовать разделители [и] вместо кавычек (одинарных или двойных)?
Идентификаторы никогда не должны содержать кавычек (если только вам не повезло больше, чем сейчас), поэтому вы удалите обычный коэффициент использования кавычек в именах и т. д.
Изменить:
Но если вызовы ORM уже параметризованы, вам не нужно об этом беспокоиться, не так ли? Использование [and] устраняет необходимость в сложном экранировании в строках C #