Как избежать простых SQL-запросов в C# для SqlServer


Я использую API, который ожидает строку SQL. Я беру пользовательский ввод, экранирую его и передаю его в API. Пользовательский ввод довольно прост. Он запрашивает значения столбцов. Вот так:

string name = userInput.Value;

затем я создаю SQL-запрос:

string sql = string.Format("SELECT * FROM SOME_TABLE WHERE Name = '{0}'",
                           name.replace("'", "''"));

Это достаточно безопасно? Если это не так, есть ли простая библиотечная функция, которая делает значения столбцов безопасными:

string sql = string.Format("SELECT * FROM SOME_TABLE WHERE Name = '{0}'",
                           SqlSafeColumnValue(name));

API использует SQLServer в качестве базы данных.

7 62

7 ответов:

поскольку использование SqlParameter не является опцией, просто замените ' на " (это две одинарные кавычки, а не одна двойная кавычка) в строковых литералах. Вот и все.

для потенциальных downvoters: перечитайте первую строку вопроса. "Использовать параметры" - это тоже была моя внутренняя реакция.

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

я использовал динамический sql (я слышу, как расстрельная команда заряжает свои винтовки) для функциональности поиска, но он сломался бы всякий раз, когда пользователь искал кого-то с фамилией, такой как "O'Reilly".

мне удалось найти обходной путь (читай "hack"):

создал скалярную функцию в sql, которая заменила одинарную кавычку двумя одинарными кавычками, эффективно избегая оскорбительной одинарной кавычки, поэтому
"...Фамилия вроде '%O'Reilly% ' и..." становится "...Фамилия вроде '%O 'Reilly%' AND..."

эта функция вызывается из sql всякий раз, когда я подозреваю, что поля могут содержать один символ кавычки ie: firstname, lastname.

CREATE FUNCTION [dbo].[fnEscapeSingleQuote]
    (@StringToCheck NVARCHAR(MAX))
RETURNS NVARCHAR(MAX)
AS
BEGIN
    DECLARE @Result NVARCHAR(MAX)
    SELECT @Result = REPLACE(@StringToCheck, CHAR(39), CHAR(39) + CHAR(39))
    RETURN @Result
END

Не очень элегантное, но оно работает, когда вы находитесь в крайнем случае.

можно заменить " на " Вместо параметризации при необходимости решения проблемы в большом количестве ad hoc sql за короткое время с минимальным риском поломки и минимальным тестированием.

лучше всего использовать параметры SQL, но тогда у вас есть ограничение на 2300 параметров для запроса. В большинстве случаев этого будет более чем достаточно. Но в редких случаях, когда вы превышаете этот предел, я рассматриваю это как вариант.

SqlCommand и Entity Framework используют exec sp_executesql....

таким образом, действительно есть альтернатива необработанным строкам с вашим собственным шаблоном экранирования. С помощью SqlCommand вы технически используете параметризованные запросы, но вы обходите ADO.Net абстракция базового кода SQL.

поэтому, хотя ваш код не предотвращает SQL-инъекцию, конечным ответом является sp_executesql, а не SqlCommand.

сказав это, я уверен, что есть специальная обработка требования для создания строки, защищенной от инъекций SQL, которая использует sp_executesql.

посмотреть: как вернуть значения из динамической хранимой процедуры SQL в Entity Framework?

просто:

const string sql = "SELECT * FROM SOME_TABLE WHERE Name = @name";

и добавить @name параметр со значением:

cmd.CommandText = sql;
cmd.Parameters.AddWithValue("@name", name);

Если вам нужно экранировать строку для запроса MSSQL, попробуйте следующее:

System.Security.SecurityElement.Escape(Value)