Достаточно ли параметров для предотвращения инъекций SQL?

83

Я проповедовал своим коллегам и здесь, в SO, о пользе использования параметров в запросах SQL, особенно в приложениях .NET. Я даже зашел так далеко, что пообещал им защиту от атак SQL-инъекций.

Но я начинаю задаваться вопросом, правда ли это на самом деле. Существуют ли какие-либо известные атаки с использованием SQL-инъекций, которые будут успешными против параметризованного запроса? Можете ли вы, например, отправить строку, которая вызывает переполнение буфера на сервере?

Конечно, есть и другие соображения, которые необходимо учитывать для обеспечения безопасности веб-приложения (например, дезинфекция пользовательского ввода и все такое), но теперь я думаю о SQL-инъекциях. Меня особенно интересуют атаки на MsSQL 2005 и 2008, поскольку они являются моими основными базами данных, но все базы данных интересны.

Изменить: чтобы прояснить, что я имею в виду под параметрами и параметризованными запросами. Под использованием параметров я подразумеваю использование «переменных» вместо построения запроса sql в строке.
Поэтому вместо этого:

SELECT * FROM Table WHERE Name = 'a name'

Делаем так:

SELECT * FROM Table WHERE Name = @Name

а затем установите значение параметра @Name в объекте запроса / команды.

Руна Гримстад
источник
мы должны уточнить, что подразумевается под параметрами (как указал Джонатан Леффлер) - я думал о параметрах хранимой процедуры, но есть ли они? parms и {0} parms ...
Стивен А. Лоу
Гораздо проще сказать, мы не используем конкатенацию для построения запроса.
Поскольку это тег asp.net, я предполагаю, что вы создаете веб-приложения. В этом случае вам также следует позаботиться об XSS-атаках и, возможно, о других
Spikolynn

Ответы:

50

Заполнители достаточно , чтобы предотвратить инъекции. Возможно, вы по-прежнему открыты для переполнения буфера, но это совершенно другой вид атаки, нежели SQL-инъекция (вектор атаки будет не синтаксисом SQL, а двоичным). Поскольку все переданные параметры будут экранированы должным образом, злоумышленник не сможет передать данные, которые будут обрабатываться как «живой» SQL.

Вы не можете использовать функции внутри заполнителей, и вы не можете использовать заполнители в качестве имен столбцов или таблиц, потому что они экранированы и заключены в кавычки как строковые литералы.

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

Тем не менее, если вы используете input для установки значения чего-то вроде security_level, тогда кто-то может просто сделать себя администраторами в вашей системе и получить доступ ко всем. Но это всего лишь базовая проверка ввода и не имеет ничего общего с SQL-инъекцией.

Адам Беллэр
источник
Ключевым моментом является понимание проблемы, поднятой ответом Стива Лоу, также отмеченным в статье, которую цитирует @mikekidder - вы должны быть осторожны, где бы ни находился динамический SQL, будь то в приложении или на сервере. Динамический SQL опасен, но его можно сделать безопасным.
Джонатан Леффлер,
«злоумышленник не может передать данные, которые будут обрабатываться как« живой »SQL». - Это не совсем так, см. Примеры ниже.
Booji Boy
Все приведенные ниже примеры определяют «параметризованный запрос», означающий, что код SQL принимает параметры. Обычное определение - это запрос, который использует вашу коллекцию параметров СУБД. За исключением ошибки СУБД, этот последний метод предотвращает внедрение SQL.
HTTP 410,
2
Я прочитал каждую ссылку. Пожалуйста, процитируйте любую ссылку, которая относится к работающей атаке путем внедрения на коллекцию параметров СУБД. Действительно, размещенная вами ссылка конкретно относится к этому подходу как к побеждению SQL-инъекции (см. Раздел «Использование типобезопасных параметров SQL»).
HTTP 410,
Привет! Не могли бы вы предоставить ссылку на грамматику Oracle SQL или что-нибудь в этом роде, чтобы доказать этот ответ. Я понимаю и полностью согласен с вами, но было бы здорово иметь официальную ссылку на документацию, грамматику и т. Д. BestRegards, Раимбек
Раимбек Рахимбек
13

Нет, риск внедрения SQL по-прежнему существует каждый раз, когда вы интерполируете непроверенные данные в запрос SQL.

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

'SELECT * FROM mytable WHERE colname = ?'

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

'SELECT * FROM ' + @tablename + ' WHERE colname IN (' + @comma_list + ')'
' ORDER BY ' + @colname'

Неважно, используете ли вы хранимые процедуры или выполняете динамические SQL-запросы непосредственно из кода приложения. Риск все еще есть.

Средство правовой защиты в этих случаях - использовать FIEO по мере необходимости:

  • Входные данные фильтра: убедитесь, что данные выглядят как допустимые целые числа, имена таблиц, имена столбцов и т. Д., Прежде чем интерполировать их.

  • Escape Output: в этом случае «вывод» означает ввод данных в SQL-запрос. Мы используем функции для преобразования переменных, используемых в качестве строковых литералов в выражении SQL, чтобы экранировать кавычки и другие специальные символы внутри строки. Мы также должны использовать функции для преобразования переменных, которые будут использоваться в качестве имен таблиц, столбцов и т. Д. Что касается другого синтаксиса, например, динамического написания целых выражений SQL, это более сложная проблема.

Билл Карвин
источник
12

Похоже, в этой теме есть некоторая путаница по поводу определения "параметризованного запроса".

  • SQL, такой как хранимая процедура, которая принимает параметры.
  • SQL, который вызывается с использованием коллекции параметров СУБД.

Учитывая первое определение, многие ссылки показывают рабочие атаки.

Но «нормальное» определение - это последнее. Учитывая это определение, я не знаю ни одной атаки с использованием SQL-инъекции, которая сработает. Это не значит, что его нет, но я его еще не видел.

Судя по комментариям, я недостаточно четко выражаюсь, поэтому вот пример, который, надеюсь, будет более ясным:

Этот подход является открытым для SQL инъекций

exec dbo.MyStoredProc 'DodgyText'

Этот подход не подходит для SQL-инъекций

using (SqlCommand cmd = new SqlCommand("dbo.MyStoredProc", testConnection))
{
    cmd.CommandType = CommandType.StoredProcedure;
    SqlParameter newParam = new SqlParameter(paramName, SqlDbType.Varchar);
    newParam.Value = "DodgyText";
    .....
    cmd.Parameters.Add(newParam);
    .....
    cmd.ExecuteNonQuery();
}
HTTP 410
источник
Не могли бы вы пояснить, что вы имеете в виду под коллекцией параметров СУБД в отличие от процедуры, принимающей параметры?
Rune Grimstad
Rune, прочтите раздел «Use Type-Safe SQL Parameters» по этой ссылке: msdn.microsoft.com/en-us/library/ms161953.aspx
HTTP 410
Я ответил на первоначальный вопрос Руне, до того, как он был отредактирован с обновлением.
mikekidder
Я прочитал и перечитал эту статью msdn о внедрении sql, и я до сих пор не понимаю, как есть разница между параметрами, которые принимает хранимая процедура, и параметрами, которые принимает динамический запрос. Помимо того, что динамические запросы являются динамическими. Вам все равно нужно привязать параметры, верно?
Rune Grimstad
Все дело в привязке. Если вы вызываете сохраненную процедуру с параметрами напрямую, входная фильтрация не выполняется. Но если вы выполняете привязку (например) с помощью коллекции параметров SqlCommand в .NET, все параметры будут отфильтрованы и обработаны как простой текст.
HTTP 410,
10

любой параметр sql строкового типа (varchar, nvarchar и т. д.), который используется для создания динамического запроса, по-прежнему уязвим

в противном случае преобразование типа параметра (например, в int, decimal, date и т. д.) должно исключить любую попытку внедрить sql через параметр

РЕДАКТИРОВАТЬ: пример, где параметр @ p1 предназначен для имени таблицы

create procedure dbo.uspBeAfraidBeVeryAfraid ( @p1 varchar(64) ) 
AS
    SET NOCOUNT ON
    declare @sql varchar(512)
    set @sql = 'select * from ' + @p1
    exec(@sql)
GO

Если @ p1 выбран из раскрывающегося списка, это потенциальный вектор атаки с использованием sql-инъекции;

Если @ p1 сформулирован программно без возможности вмешательства пользователя, то это не потенциальный вектор атаки sql-инъекции.

Стивен А. Лоу
источник
Нет; все дело в том, что строка, переданная в СУБД, не является частью оператора SQL. Следовательно, значение в строке не имеет значения для интерпретации SQL - только для значений, на которые ссылается SQL.
Джонатан Леффлер,
Я тоже так вижу параметры. Они должны предотвратить эту проблему.
Rune Grimstad
2
Стивен прав, если, например, вы передаете строку в sp, который использует ее для запуска чего-то вроде sp_executeSql (sql server), тогда у вас все еще есть риск внедрения sql.
alexmac
@ Стивен: это не параметр SQL; вам понадобится заполнитель (знак вопроса) вместо конкатенации строк. И SQL не позволяет указывать имя таблицы по заполнителю. Это чистая уязвимость SQL-инъекций - изначальная проблема.
Джонатан Леффлер,
@Steven: возможно, термин "параметр" слишком часто перегружался. : D
Джонатан Леффлер
6

Переполнение буфера - это не SQL-инъекция.

Параметризованные запросы гарантируют защиту от SQL-инъекций. Они не гарантируют, что на вашем сервере SQL нет возможных эксплойтов в виде ошибок, но ничто не гарантирует этого.

Blorgbeard отсутствует
источник
2

Ваши данные небезопасны, если вы используете динамический sql в любой форме или форме, потому что разрешения должны быть на уровне таблицы. Да, вы ограничили тип и количество инъекционных атак по этому конкретному запросу, но не ограничили доступ, который пользователь может получить, если он или она найдет путь в систему, и вы полностью защищены для внутренних пользователей, имеющих доступ к тому, в чем они не должны чтобы совершить мошенничество или украсть личную информацию для продажи. Динамический SQL любого типа - опасная практика. Если вы используете нединамические хранимые процессы, вы можете устанавливать разрешения на уровне процесса, и ни один пользователь не может делать ничего, кроме того, что определено процессами (кроме системных администраторов, конечно).

HLGEM
источник
Итак, урок заключается в том, что если вам необходимо использовать динамический sql, делайте это только внутри хранимой процедуры. +1 хороший совет!
Стивен А. Лоу
1
Нет - динамический SQL в хранимых процессах может по-прежнему вносить недостатки SQL-инъекции, интерполируя непроверенные данные в динамический запрос.
Bill Karwin
Нет урока в том, чтобы никогда не использовать динамический SQl
HLGEM
@HLGEM - верно, автомобили попадают в дорожно-транспортные происшествия, поэтому мы никогда не должны использовать автомобили.
Билл Карвин,
Но динамический SQL в хранимой процедуре запускается (по умолчанию) с разрешения вызывающей стороны, в отличие от статического SQL, который запускается с разрешения владельца хранимой процедуры. Это важное различие.
HTTP 410,
1

Сохраненная процедура может быть уязвима для специальных типов SQL-инъекций через переполнение / усечение, см .: Внедрение, активируемое усечением данных здесь:

http://msdn.microsoft.com/en-us/library/ms161953.aspx

Booji Boy
источник
Если вы прочтете статью подробно, то увидите, что использование коллекции параметров SQL Server предотвращает эту атаку. И это обычное определение «параметризованного запроса» - он использует коллекцию параметров СУБД.
HTTP 410,
1

Просто помните, что с помощью параметров вы можете легко сохранить строку или произнести имя пользователя, если у вас нет политик, "); drop table users; -"

Само по себе это не причинит никакого вреда, но вам лучше знать, где и как эта дата используется в дальнейшем в вашем приложении (например, сохраняется в cookie, извлекается позже для других целей.

нет
источник
1

Вы можете запустить динамический sql в качестве примера

DECLARE @SQL NVARCHAR(4000);
DECLARE @ParameterDefinition NVARCHAR(4000);

SELECT  @ParameterDefinition = '@date varchar(10)'

SET @SQL='Select CAST(@date AS DATETIME) Date'

EXEC sp_executeSQL @SQL,@ParameterDefinition,@date='04/15/2011'
Мохамед Аббас
источник