Цей код працює належним чином, оскільки це:
- Параметризовані та
- Не робить жодного динамічного SQL
Для того, щоб SQL Injection працювала, вам потрібно створити рядок запиту (який ви не робите), а не переводити окремі апострофи ( '
) в escape-apostrophes ( ''
) (ті, які виводяться через вхідні параметри).
У вашій спробі передати "компрометоване" значення, 'Male; DROP TABLE tblActor'
рядок - це саме те, що є звичайним рядком.
Тепер, якщо ви щось робили:
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT fields FROM table WHERE field23 = '
+ @InputParam;
EXEC(@SQL);
тоді це може бути сприйнятливим до ін'єкції SQL, оскільки цей запит не знаходиться в поточному, попередньо проаналізованому контексті; на даний момент запит - це лише інший рядок. Таким чином, значення @InputParam
може бути '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
і те, що може представляти проблему, оскільки цей запит буде надано та виконується як:
SELECT fields FROM table WHERE field23 = '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
Це одна (з декількох) головних причин використання збережених процедур: за своєю суттю більш захищена (ну, поки ви не обходите цю безпеку, будуючи запити, як я показав вище, не перевіряючи значення будь-яких використовуваних параметрів). Хоча якщо вам потрібно створити Dynamic SQL, кращим способом є його параметризація, використовуючи sp_executesql
:
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT fields FROM table WHERE field23 = @SomeDate_tmp';
EXEC sp_executesql
@SQL,
N'SomeDate_tmp DATETIME',
@SomeDate_tmp = @InputParam;
Використовуючи цей підхід, хто - то намагається передати '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
для DATETIME
вхідного параметра отримати помилку при виконанні процедури, що. Або навіть якщо Збережена процедура прийнята @InputParameter
як NVARCHAR(100)
, вона повинна буде перетворитись на a DATETIME
, щоб перейти в цей sp_executesql
виклик. І навіть якщо параметр в Dynamic SQL є рядковим типом, вступаючи в процедуру зберігання, в першу чергу, будь-який єдиний апостроф автоматично переходить у подвійний апостроф.
Існує менш відомий тип атаки, при якому зловмисник намагається заповнити поле введення апострофами, таким чином, що рядок всередині збереженої процедури, який буде використовуватися для побудови динамічного SQL, але який оголошений занадто малим, не може вмістити все і виштовхує закінчується апостроф і якось закінчується правильною кількістю апострофів, щоб більше не «уникнути» всередині рядка. Це називається SQL Truncation, про який говорилося в статті журналу MSDN під назвою "Нові атаки скорочення SQL і як їх уникнути", Бала Неерумалла, але стаття більше не в Інтернеті. Випуск, що містить цю статтю - журнал MSDN за листопад 2006 року - доступний лише у вигляді довідкового файлу Windows (in .chmформат). Якщо ви завантажите його, він може не відкритися через налаштування безпеки за замовчуванням. Якщо це сталося, то клацніть правою кнопкою миші файл MSDNMagazineNovember2006en-us.chm та виберіть "Властивості". На одній із цих вкладок буде опція "Довіряти файлу цього типу" (чи тому подібному), який потрібно перевірити / включити. Натисніть кнопку "ОК", а потім спробуйте відкрити .chm файл ще раз.
Іншим варіантом атаки усікання є припущення, що локальна змінна використовується для зберігання "безпечного" значення, що надається користувачем, оскільки у нього були подвійні котирування, щоб уникнути, щоб заповнити цю локальну змінну та розмістити єдину цитату в кінці. Ідея тут полягає в тому, що якщо локальна змінна не має належного розміру, в кінці не буде достатньо місця для другої одноцитати, залиште змінну, що закінчується однією цитатою, яка потім поєднується з одноцитатою, закінчує буквальне значення в Dynamic SQL, перетворюючи це закінчення одиночної цитати у вбудований уникнутий одноцитат, а рядковий літерал у Dynamic SQL потім закінчується наступним одноцитатом, який повинен був розпочати наступний літеральний рядок. Наприклад:
-- Parameters:
DECLARE @UserID INT = 37,
@NewPassword NVARCHAR(15) = N'Any Value ....''',
@OldPassword NVARCHAR(15) = N';Injected SQL--';
-- Stored Proc:
DECLARE @SQL NVARCHAR(MAX),
@NewPassword_fixed NVARCHAR(15) = REPLACE(@NewPassword, N'''', N''''''),
@OldPassword_fixed NVARCHAR(15) = REPLACE(@OldPassword, N'''', N'''''');
SELECT @NewPassword AS [@NewPassword],
REPLACE(@NewPassword, N'''', N'''''') AS [REPLACE output],
@NewPassword_fixed AS [@NewPassword_fixed];
/*
@NewPassword REPLACE output @NewPassword_fixed
Any Value ....' Any Value ....'' Any Value ....'
*/
SELECT @OldPassword AS [@OldPassword],
REPLACE(@OldPassword, N'''', N'''''') AS [REPLACE output],
@OldPassword_fixed AS [@OldPassword_fixed];
/*
@OldPassword REPLACE output @OldPassword_fixed
;Injected SQL-- ;Injected SQL-- ;Injected SQL--
*/
SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
+ @NewPassword_fixed + N''' WHERE [TableNameID] = '
+ CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
+ @OldPassword_fixed + N''';';
SELECT @SQL AS [Injected];
Ось тепер динамічний SQL, який потрібно виконати:
UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';
Той самий динамічний SQL у більш читаному форматі:
UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';
Injected SQL--';
Виправити це легко. Виконайте одне з наступних дій:
- НЕ ВИКОРИСТОВУЙТЕ ДИНАМІЧНИЙ SQL БЕЗ ЗАБЕЗПЕЧНО АБСОЛЮТНО ! (Я перелічу це першим, тому що це дійсно має бути першим, що слід розглянути).
- Належне розмір локальної змінної (тобто повинен бути вдвічі більшим за вхідний параметр, на випадок, якщо всі передані символи є одинарними лапками).
Не використовуйте локальну змінну для зберігання "фіксованого" значення; просто вкладіть REPLACE()
безпосередньо у створення Dynamic SQL:
SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
+ REPLACE(@NewPassword, N'''', N'''''') + N''' WHERE [TableNameID] = '
+ CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
+ REPLACE(@OldPassword, N'''', N'''''') + N''';';
SELECT @SQL AS [No SQL Injection here];
Динамічний SQL більше не порушений:
UPDATE dbo.TableName SET [Password] = N'Any Value ....''' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';
Примітки про приклад Trunction вище:
- Так, це дуже надуманий приклад. Внести в дію лише 15 символів не так багато. Звичайно, це може
DELETE tableName
бути руйнівним, але менше шансів додати користувача заднього дверей або змінити пароль адміністратора.
- Цей тип атаки, ймовірно, вимагає знання коду, назв таблиць тощо. Менш ймовірно, що це робитиме випадковий незнайомець / скрипт-малюк, але я працював у місці, на яке напав досить засмучений колишній співробітник, який знав про вразливість на одній конкретній веб-сторінці, про яку ніхто інший не знав. Це означає, що іноді зловмисники мають інтимні знання про систему.
- Зрозуміло, перезавантаження пароля кожного, ймовірно, буде розслідувано, що може призвести до того, що компанія може відмовитись від того, що трапляється напад, але це може все-таки забезпечити достатньо часу для введення в користування заднього дверей або, можливо, пізніше отримати додаткову інформацію для використання / використання.
- Навіть якщо цей сценарій здебільшого є академічним (тобто, мабуть, не відбудеться в реальному світі), все одно це неможливо.
Для отримання більш детальної інформації, що стосується інжекцій SQL (охоплює різні RDBMS та сценарії), дивіться наступне з проекту безпеки веб-застосунку (OWASP):
Тестування на ін'єкцію SQL
Відповідна відповідь на переповнення стека на ін'єкцію SQL та обрізання SQL:
Наскільки безпечним є T-SQL після заміни символу 'escape?
EXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'