Як уникнути простих SQL-запитів у C # для SqlServer


78

Я використовую 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 як базу даних.


Параметризуйте це! Google пошук так для «ін'єкції SQL» Edit: У відповідь на Сева Алексєєв, SO відповісти з характером 8 ін'єкцій
ГБН

3
Вам потрібно використовувати параметри.
Слакс

2
@SLaks, очевидно, що API не дозволяє цього. Можливо, йому потрібен новий API.
C. Ross

3
@sri ви отримаєте кращі відповіді, якщо поясніть, чому ви не можете / не хочете використовувати параметри.
Foole

чи є спосіб параметризувати запити зі списками? SELECT [Name], [Value] FROM [SomeTable] WHERE [Name] IN (@ListOfNames) Я маю на увазі якийсь інший спосіб, а не var listParNames = listNames.Select(name => { var pname = string.Format("@n{0}", cmd.Parameters.Count); cmd.Parameters.AddWithValue(pname, name); return pname; }); cmd.CommandText = string.Format("SELECT [Name], [Value] FROM [SomeTable] WHERE [Name] in ({0})", string.Join(",", listParNames.ToArray())); більше як прямийcmd.Parameters.AddWithValue("@ListOfNames", listNames.ToArray());
mizuki nakeshu

Відповіді:


137

Оскільки використання SqlParameter не є варіантом, просто замініть 'на' (це дві одинарні лапки, а не одна подвійна лапка) у рядкових літералах. Це воно.

Потенційним виборцям: перечитайте перший рядок питання. "Параметри використання" також була моєю реакцією кишечника.

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


13
Є трохи 'після Роберта. Це як. При правильному цитуванні неортодоксальне ім’я Боббі зберігатиметься в базі даних повністю.
Сева Олексієв

46
Сева Олексієв дякую, так, дуже, що прочитали моє запитання і спробували на нього відповісти. Дякую, що не проповідували мені про "найкращі практики". І дякую за те, що не намагалися мене «виховувати» Дійсно, дякую!
sc45

12
Гм, на протокол, я все ще думаю, що параметри - це шлях :) Однак я займаюся цим бізнесом досить довго, щоб зрозуміти існування обмежень на ваші проекти. Був там зробив те.
Сева Олексієв

13
@Seva Хоча всі знають, що наведений вище код піддається ін’єкціям, іноді ви пов’язані з API, який просто не дозволяє вам слідувати найкращим практикам. Дуже часто програмісти турбуються про речі, які не входять у коло конкретного завдання. Хороша відповідь на конкретне питання.
Кіт Адлер,

9
Чудова відповідь! Нам потрібні більше таких людей, як ви, і менше людей, які протегують та навчають! Я підтримав вас і підтримав іншого безглуздого хлопця
Крістіана

0

Я використовував динамічний sql (я чую, як стрілець заряджає свої гвинтівки) для функціональності пошуку, але він ламався, коли користувач шукав когось із таким прізвищем, як "O'Reilly".

Мені вдалося розібратися із робочим процесом (читайте "рубати"):

Створив скалярну функцію в sql, яка замінила одинарну лапку двома одинарними лапками, ефективно уникнувши одинарної лапки, тому
"... Прізвище ПОДОБАЄТЬСЯ"% O'Reilly% 'І ... "стає" ... Прізвище ПОДОБАЄТЬСЯ '% O''Reilly%' І ... "

Ця функція отримує виклик у sql, коли я підозрюю, що поля можуть містити одинарні лапки, тобто: ім'я, прізвище.

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

Не дуже елегантний чи ефективний, але він працює, коли ви стискаєтеся.


0

Можливо, ви захочете замінити "на" замість параметризації, коли потрібно вирішити проблему у великій кількості спеціальних sql за короткий час з мінімальним ризиком поломки та мінімальним тестуванням.


0

Використання SqlCommand та Entity Framework exec sp_executesql....

Отже, насправді є альтернатива необробленим рядкам із вашим власним шаблоном екранування, мабуть. За допомогою SqlCommand ви технічно використовуєте параметризовані запити, але обходите абстракцію ADO.Net базового коду SQL.

Отже, хоча ваш код не перешкоджає SQL Injection, остаточна відповідь - sp_executesql, а не SqlCommand.

Сказавши це, я впевнений, що існують особливі вимоги до обробки для створення рядка, захищеного від ін’єкцій SQL, який використовує sp_executesql.

див .: Як повернути значення з динамічної збереженої процедури SQL в Entity Framework?


-4

Просто:

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

і додайте @nameпараметр зі значенням:

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

36
Йому все ще потрібно дістати фактичний оператор SQL з об'єкта команди, щоб передати його в API.
reustmd

Він прямо заявляє, що використовує API, який приймає рядок SQL. Він НЕ використовує ADO.Net.
MHollis

@MHollis точно; що є першою проблемою, яку вони повинні виправити. Вибачте, але немає бажаного способу зробити це без параметрів. Є причина, чому люди кричать про параметри.
Марк Гравелл

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.