Очистить имя таблицы / столбца в динамическом SQL в .NET? (Предотвратить атаки SQL-инъекций) - PullRequest
10 голосов
/ 11 марта 2012

Я генерирую некоторый динамический SQL и хотел бы убедиться, что мой код безопасен от SQL-инъекций .

Для аргумента вот минимальный пример того, как он генерируется:

var sql = string.Format("INSERT INTO {0} ({1}) VALUES (@value)",
    tableName, columnName);

В приведенном выше примере tableName, columnName и все, что связано с @value, получены из ненадежного источника.Поскольку используются заполнители, @value защищен от атак SQL-инъекций и может быть проигнорирован.(Команда выполняется с помощью SqlCommand.)

Однако tableName и columnName не могут быть связаны в качестве заполнителей и поэтому уязвимы для атак внедрения.Поскольку это «действительно динамичный» сценарий, нет белого списка tableName или columnName.

Вопрос, таким образом:

Существует ли стандарт , встроенный способ проверить и / или продезинфицировать tableName и columnName?(SqlConnection, или вспомогательный класс и т. Д.) Если нет, каков хороший способ выполнить эту задачу без с использованием сторонней библиотеки?

Примечания:

  • Все идентификаторы SQL, включая схему, должны быть приняты: например, [schema].[My Table].column столь же "безопасен", как table1.
  • Может либо очистить идентификаторы, либо обнаружить неверный идентификатор.(Нет необходимости гарантировать, что таблица / столбец действительно действительна в контексте; результирующий SQL может быть недействительным, но должен быть «безопасным».)

Обновление:

Просто нашел это и подумал, что это несколько интересно: в .NET4 (EF4?) Есть функция SqlFunctions.QuoteName .Ладно, это не действительно поможет мне здесь ...

Ответы [ 3 ]

21 голосов
/ 28 сентября 2012

Я не уверен, что вы все еще изучаете это, но класс DbCommandBuilder предоставляет метод QuoteIdentifier для этой цели. Основным преимуществом этого является то, что он не зависит от базы данных и не связан с беспорядком в RegEx.

Начиная с .NET 4.5, у вас есть все необходимое для очистки имен таблиц и столбцов, просто используя ваш объект DbConnection:

DbConnection connection = GetMyConnection(); // Could be SqlConnection
DbProviderFactory factory = DbProviderFactories.GetFactory(connection);

// Sanitize the table name
DbCommandBuilder commandBuilder = factory.CreateCommandBuilder();

string tableName = "This Table Name Is Long And Bad";
string sanitizedTableName = commandBuilder.QuoteIdentifier(tableName);

IDbCommand command = connection.CreateCommand();
command.CommandText = "SELECT * FROM " + sanitizedTableName;

// Becomes 'SELECT * FROM [This Table Name Is Long And Bad]' in MS-SQL,
// 'SELECT * FROM "This Table Name Is Long And Bad"' in Oracle, etc.

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

5 голосов
/ 11 марта 2012

Поскольку вы используете SqlConnection, предполагается, что это база данных SQL Server.

Исходя из этого предположения, вы можете проверять имена таблиц и полей с помощью регулярного выражения, которое следует правилам идентификатора SQL Server, как определено в MSDN . Хотя я полный и полный новичок в регулярных выражениях, я нашел этот, который должен быть ближе:

[\p{L}{\p{Nd}}$#_][\p{L}{\p{Nd}}@$#_]*

Однако регулярное выражение не будет адресовать ключевые слова SQL Server и не гарантирует, что таблица и / или столбец действительно существуют (хотя вы указали, что это не было большой проблемой).

Если бы это было мое приложение, я бы сначала убедился, что конечный пользователь не пытался выполнить инъекцию, отклонив любой запрос, содержащий точки с запятой (;).

Далее я бы проверил существование таблицы, удалив допустимые разделители имен (", ', [,]), разделив имя таблицы по периоду, чтобы увидеть, была ли указана схема, и выполнив запрос к INFORMATION_SCHEMA.TABLES. определить наличие таблицы.

Например:

SELECT 1 
FROM   INFORMATION_SCHEMA.TABLES 
WHERE  TABLE_NAME = 'tablename' 
AND    TABLE_SCHEMA = 'tableschema'

Если вы создаете этот запрос с использованием параметров, вам следует дополнительно защитить себя от внедрения.

Наконец, я бы проверил существование каждого имени столбца, выполнив аналогичный набор шагов, используя только INFORMATION_SCHEMA.COLUMNS, чтобы определить достоверность столбца (столбцов), как только таблица будет определена как действительная.

Я бы, вероятно, выбрал список допустимых столбцов для этой таблицы из SQL Server, а затем проверил, что каждый столбец запроса был в списке в моем коде. Таким образом, вы сможете точно определить, в каких столбцах произошла ошибка, и предоставить эту обратную связь пользователю.

1 голос
/ 16 апреля 2015

Для SQL Server довольно просто санировать идентификатор:

// To make a string safe to use as an SQL identifier :
// 1. Escape single closing bracket with double closing bracket
// 2. Wrap in square brackets
string.Format("[{0}]", identifier.Replace("]", "]]"));

После того, как он заключен в квадратные скобки и экранирован, единственной вещью, которая не будет работать в качестве идентификатора, является пустая / пустая строка.

...