Почему SQL-инъекция не происходит по этому запросу внутри хранимой процедуры?

18

Я сделал следующую хранимую процедуру:

ALTER PROCEDURE usp_actorBirthdays (@nameString nvarchar(100), @actorgender nvarchar(100))
AS
SELECT ActorDOB, ActorName FROM tblActor
WHERE ActorName LIKE '%' + @nameString + '%'
AND ActorGender = @actorgender

Теперь я попытался сделать что-то вроде этого. Может быть, я делаю это неправильно, но я хочу быть уверен, что такая процедура может предотвратить любую инъекцию SQL:

EXEC usp_actorBirthdays 'Tom', 'Male; DROP TABLE tblActor'

На рисунке ниже показано, что SQL выше выполняется в SSMS и результаты отображаются правильно, а не ошибка:

введите описание изображения здесь

Кстати, я добавил эту часть после точки с запятой после выполнения запроса. Затем я выполнил это снова, но когда я проверил, существует ли таблица tblActor или нет, она все еще была там. Я делаю что-то неправильно? Или это действительно инъекционный? Я думаю, что я пытаюсь спросить здесь также, является ли хранимая процедура, как это безопасно? Спасибо.

Ravi
источник
Вы пробовали этоEXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'
MarmiK

Ответы:

38

Этот код работает правильно, потому что это:

  1. Параметризованный и
  2. Не делать динамический SQL

Чтобы SQL-инъекция работала, вы должны построить строку запроса (которую вы не делаете) и не переводить одиночные апострофы ( ') в escape-апострофы ( '') (экранированные через входные параметры).

В вашей попытке передать «скомпрометированное» значение, '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;

Это одна (из нескольких) основных причин использования хранимых процедур: по своей природе более безопасная (ну, пока вы не обойдете эту безопасность, создавая запросы, как я показал выше, без проверки значений каких-либо используемых параметров). Хотя, если вам нужно построить динамический 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вызову. И даже если параметр в динамическом SQL является строковым типом, то при входе в хранимую процедуру в первую очередь любой отдельный апостроф автоматически превращается в двойной апостроф.

Существует менее известный тип атаки, при котором злоумышленник пытается заполнить поле ввода апострофами, так что строка внутри хранимой процедуры, которая будет использоваться для создания динамического SQL, но которая объявлена ​​слишком маленькой, не может соответствовать всем и выталкивает заключительный апостроф и каким-то образом заканчивается правильным количеством апострофов, чтобы больше не «ускользать» внутри строки. Это называется усечением SQL, о котором говорилось в статье журнала MSDN под названием «Новые атаки с усечением SQL и как их избежать», автор Bala Neerumalla, но эта статья больше не находится в сети. Выпуск, содержащий эту статью - выпуск MSDN Magazine за ноябрь 2006 года - доступен только в виде файла справки Windows (в .chmформат). Если вы загрузите его, он может не открыться из-за настроек безопасности по умолчанию. Если это произойдет, щелкните правой кнопкой мыши файл MSDNMagazineNovember2006en-us.chm и выберите «Свойства». На одной из этих вкладок будет опция «Доверять этому типу файла» (или что-то в этом роде), которую необходимо проверить / включить. Нажмите кнопку «ОК» и попробуйте снова открыть файл .chm .

Еще один вариант атаки усечения заключается в том, что предполагается, что локальная переменная используется для хранения «безопасного» пользовательского значения, поскольку в нем были одинарные кавычки, удвоенные для экранирования, чтобы заполнить эту локальную переменную и поместить одинарные кавычки. в конце. Идея заключается в том, что если локальная переменная имеет неправильный размер, в конце не хватит места для второй одинарной кавычки, оставьте переменную, заканчивающуюся одинарной кавычкой, которая затем объединяется с одинарной кавычкой, которая завершает литеральное значение в динамическом SQL, превращая эту конечную одинарную кавычку во встроенную экранированную одинарную кавычку, а строковый литерал в динамическом 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--';

Исправить это легко. Просто выполните одно из следующих действий:

  1. НЕ ИСПОЛЬЗУЙТЕ ДИНАМИЧЕСКИЙ SQL, ЕСЛИ НЕ СЛЕДУЕТ НЕОБХОДИМЫ! (Я перечисляю это сначала, потому что это действительно должно быть первым, что нужно рассмотреть).
  2. Правильный размер локальной переменной (т. Е. Должен быть в два раза больше размера входного параметра, на случай, если все переданные символы являются одинарными кавычками).
  3. Не используйте локальную переменную для хранения «фиксированного» значения; Просто поместите REPLACE()непосредственно в создание динамического 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 выше:

  1. Да, это очень надуманный пример. Не так много можно сделать, используя только 15 символов для ввода. Конечно, может DELETE tableNameбыть разрушительным, но с меньшей вероятностью добавит секретного пользователя или сменит пароль администратора.
  2. Этот тип атаки, вероятно, требует знания кода, имен таблиц и т. Д. Менее вероятно, что это сделает случайный незнакомец / script-kiddie, но я работал в месте, которое было атаковано довольно расстроенным бывшим сотрудником, который знал об уязвимости на одной конкретной веб-странице, о которой никто не знал. Это означает, что иногда злоумышленники имеют глубокие знания о системе.
  3. Конечно, восстановление пароля каждого пользователя, вероятно, будет расследовано, что может предупредить компанию о том, что происходит атака, но может все же предоставить достаточно времени для внедрения второстепенного пользователя или, возможно, получить дополнительную информацию для использования / использования позже.
  4. Даже если этот сценарий в основном академический (т. Е. Вряд ли произойдет в реальном мире), это все же не невозможно.

Для получения более подробной информации, связанной с SQL-инъекцией (охватывающей различные РСУБД и сценарии), см. Следующее в открытом проекте безопасности веб-приложений (OWASP):
Тестирование на SQL-инъекцию

Ответ по переполнению стека по SQL-инъекции и усечению SQL:
Насколько безопасен T-SQL после замены «escape-символа»?

Соломон Руцкий
источник
2
О, большое спасибо, это отличный ответ. Теперь я понимаю. Мне бы очень хотелось увидеть технику, которую вы упомянули в конце, когда злоумышленник пытается заполнить поле ввода апострофами, если вы можете его найти. Заранее спасибо. Я собираюсь оставить это открытым, на случай, если вы его не найдете, я выберу это в качестве ответа.
Рави
1
@Ravi Я нашел ссылку, но она больше не попадает в статью, поскольку все они теперь заархивированы. Но я добавил некоторую информацию и полезные ссылки, и я все еще пытаюсь найти статью в этих архивах.
Соломон Руцкий
1
Спасибо srutzsky, я прочитаю статью OWASP и тесты для инъекций. Если я правильно помню, в 'mutillidae', уязвимом веб-приложении для тестирования безопасности, есть SQL-инъекция, которую я выполнил в колледже со строкой 'OR 1 = 1', что в mutillidae привело к тому, что я вошел в веб-приложение от имени администратора. думать. Именно тогда я впервые познакомился с SQL-инъекцией.
Рави
1
Я также не могу просмотреть файл .chm, но спасибо за этот прекрасный ответ и за все полезные ссылки, включая ссылку из stackoverflow и ссылку из OWASP. Я прочитал это сегодня и многому научился от этого.
Рави
2

Дело в том, что вы вообще не путаете данные с командой. Значения параметров никогда не рассматриваются как часть команды и, следовательно, никогда не выполняются.

Я написал об этом в блоге по адресу: http://blogs.lobsterpot.com.au/2015/02/10/sql-injection-the-golden-rule/

Роб Фарли
источник
Спасибо, Роб, у меня есть эта закладка для чтения позже в тот же день.
Рави