Предотвращают ли хранимые процедуры SQL-инъекцию?

83

Правда ли, что хранимые процедуры предотвращают атаки SQL-инъекций на базы данных PostgreSQL? Я провел небольшое исследование и выяснил, что SQL Server, Oracle и MySQL не защищены от внедрения SQL, даже если мы используем только хранимые процедуры. Однако эта проблема не существует в PostgreSQL.

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

Am1rr3zA
источник
4
Как ни странно, главные ответы здесь в основном OT, связанные с SQL Server, в то время как вопрос о Postgres . Вот соответствующий ответ для Postgres: dba.stackexchange.com/questions/49699/… . Есть несколько других, попробуйте поиск: dba.stackexchange.com/…
Эрвин Брандштеттер
@ErwinBrandstetter оригинальный вопрос не был помечен (ОП) postgres и был - и все еще - упоминает несколько других СУБД. Я полагаю, что причина различных ответов сосредоточена на других СУБД. Я предлагаю вам добавить еще один ориентированный на Postgres.
ypercubeᵀᴹ
@ ypercubeᵀᴹ: я добавлю ответ, когда найду время. Тем временем я обновил dba.stackexchange.com/questions/49699/…, чтобы сделать его более понятным и полным.
Эрвин Брандштеттер

Ответы:

71

Нет, хранимые процедуры не предотвращают внедрение SQL. Вот фактический пример (из собственного приложения, созданного мной, где я работаю) хранимой процедуры, которая, к сожалению, допускает внедрение SQL:

Этот код сервера SQL:

CREATE PROCEDURE [dbo].[sp_colunmName2]   
    @columnName as nvarchar(30),
    @type as nvarchar(30), 
    @searchText as nvarchar(30)           
AS
BEGIN
    DECLARE @SQLStatement NVARCHAR(4000)
    BEGIN
        SELECT @SQLStatement = 'select * from Stations where ' 
            + @columnName + ' ' + @type + ' ' + '''' + @searchText + '''' 
        EXEC(@SQLStatement)
    END      
END
GO

примерно эквивалентно postgres:

CREATE or replace FUNCTION public.sp_colunmName2 (
    columnName  varchar(30),
    type varchar(30), 
    searchText  varchar(30) ) RETURNS SETOF stations LANGUAGE plpgsql            
AS
$$
DECLARE SQLStatement VARCHAR(4000);
BEGIN
    SQLStatement = 'select * from Stations where ' 
            || columnName || ' ' || type || ' ' || ''''|| searchText || '''';
    RETURN QUERY EXECUTE  SQLStatement;
END
$$;

Идея разработчика состояла в том, чтобы создать универсальную процедуру поиска, но в результате предложение WHERE может содержать все, что хочет пользователь, что позволяет посещать маленькие таблицы Бобби .

Используете ли вы операторы SQL или хранимую процедуру, не имеет значения. Важно то, использует ли ваш SQL параметры или конкатенированные строки. Параметры предотвращают внедрение SQL; объединенные строки позволяют SQL-инъекцию.

Kyralessa
источник
46

Атаки SQL-инъекцией - это атаки, в которых ненадежный ввод представляет собой непосредственно добавленные запросы, что позволяет пользователю эффективно выполнять произвольный код, как показано в этом каноническом комиксе XKCD.

Таким образом, мы получаем ситуацию:

userInput = getFromHTML # "Robert ') Удалить таблицу студентов; -"

Query = "Выбрать * из студентов, где studentName =" + userInput

Хранимые процедуры, как правило, являются хорошей защитой от атак внедрения SQL, поскольку входящие параметры никогда не анализируются.

В хранимой процедуре, в большинстве БД (и программ, не забывайте, что предварительно скомпилированные запросы считаются хранимыми процедурами) выглядят следующим образом:

 

создать сохраненную процедуру foo (
выберите * из студентов, где studentName =: 1
);

Затем, когда программа желает получить доступ, она вызывает foo(userInput)и счастливо получает результат.

Хранимая процедура не является волшебной защитой от SQL-инъекций, поскольку люди вполне могут писать плохие хранимые процедуры. Тем не менее, предварительно скомпилированные запросы, хранящиеся ли они в базе данных или в программе, намного труднее открыть дыры в безопасности, если вы понимаете, как работает SQL-инъекция.

Вы можете прочитать больше о SQL-инъекции:

Брайан Баллсун-Стэнтон
источник
29

Да, в некоторой степени.
Хранимые процедуры сами по себе не помешают внедрению SQL.

Позвольте мне сначала процитировать SQL-инъекцию из OWASP

Атака SQL-инъекции состоит из вставки или «внедрения» SQL-запроса через входные данные от клиента к приложению. Успешный SQL-инъекционный эксплойт может считывать конфиденциальные данные из базы данных, изменять данные базы данных (вставлять / обновлять / удалять), выполнять операции администрирования базы данных (например, выключать СУБД), восстанавливать содержимое данного файла, присутствующего в файле СУБД система и в некоторых случаях выдает команды операционной системе. Атаки с использованием SQL-инъекций представляют собой тип атаки с использованием инъекций, в которой команды SQL вводятся во входные данные плоскости данных для выполнения предопределенных команд SQL.

Вы должны санировать вводимые пользователем данные и не объединять операторы SQL, даже если вы используете хранимую процедуру.

Джефф Эттвуд объяснил последствия объединения SQL в « Дайте мне параметризованный SQL, или дайте мне смерть »

Ниже приводится интересный мультфильм, который мне приходит в голову всякий раз, когда я слышу SQL-инъекцию. альтернативный текст Думаю, вы поняли :-)

Взгляните на Шпаргалку по предотвращению инъекций SQL , методы предотвращения которых четко объяснены ...

CoderHawk
источник
12

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

Хранимые процедуры добавляют дополнительный уровень безопасности, применяя неверный синтаксис при объединении, но не являются «более безопасными», если в них используется, скажем, динамический SQL.

Итак, ваш код выше вызван объединением этих строк

  • exec sp_GetUser '
  • x' AND 1=(SELECT COUNT(*) FROM Client); --
  • ' , '
  • monkey
  • '

Это дает неверный синтаксис, к счастью

Параметризация это дало бы

exec sp_GetUser 'x'' AND 1=(SELECT COUNT(*) FROM Client); --' , 'monkey'

Это означает

  • @UserName знак равно x' AND 1=(SELECT COUNT(*) FROM Client); --
  • @Password знак равно monkey

Теперь в приведенном выше коде вы не получите строк, потому что я предполагаю, что у вас нет пользователя x' AND 1=(SELECT COUNT(*) FROM Client); --

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

...
SET @sql = 'SELECT userName from users where userName = ''' + 
               @UserName + 
               ''' and userPass = ''' +
               @Password +
               ''''
EXEC (@sql)
....

Итак, как показано, конкатенация строк является главным врагом для внедрения SQL

Хранимые процедуры добавляют инкапсуляцию, обработку транзакций, ограниченные разрешения и т. Д., Но их все еще можно использовать для внедрения SQL.

Вы можете посмотреть на переполнение стека для получения дополнительной информации о параметризации

GBN
источник
10

«Атака инъекции SQL происходит , когда пользовательский ввод неправильно закодирован. Как правило, ввод данных пользователя некоторые данные пользователя отправляемые с ее запросом, то есть значения в $_GET, $_POST, $_COOKIE, $_REQUESTили $_SERVERмассивы. Однако, пользовательский ввод могут также поступать из множества других источники, такие как сокеты, удаленные веб-сайты, файлы и т. д. Поэтому вы должны действительно рассматривать все, кроме констант (например 'foobar'), как ввод данных пользователем ".

Недавно я тщательно изучал эту тему и хотел бы поделиться с другими довольно интересным материалом, чтобы сделать этот пост более полным и поучительным для всех.



С YouTube


Из Википедии


От OWASP


Из руководства по PHP


От Microsoft и Oracle


Переполнение стека


Сканер SQL-инъекций

Илья Ростовцев
источник
2

Хранимые процедуры магическим образом не предотвращают внедрение SQL, но они значительно упрощают его предотвращение. Все, что вам нужно сделать, это что-то вроде следующего (пример Postgres):

CREATE OR REPLACE FUNCTION my_func (
  IN in_user_id INT 
)
[snip]
  SELECT user_id, name, address FROM my_table WHERE user_id = in_user_id; --BAM! SQL INJECTION IMMUNE!!
[snip]

Это оно! Проблема возникает только при формировании запроса с помощью конкатенации строк (например, динамический SQL), и даже в этих случаях вы можете связать! (Зависит от базы данных.)

Как избежать внедрения SQL в ваш динамический запрос:

Шаг 1) Спросите себя, действительно ли вам нужен динамический запрос. Если вы соединяете строки только для того, чтобы установить ввод, то вы, вероятно, делаете это неправильно. (Из этого правила есть исключения - одно исключение относится к отчетам о запросах в некоторых базах данных. У вас могут быть проблемы с производительностью, если вы не заставляете его скомпилировать новый запрос при каждом выполнении. Но изучите эту проблему, прежде чем переходить к этому. )

Шаг 2) Изучите правильный способ установки переменной для вашей конкретной СУБД. Например, Oracle позволяет вам делать следующее (цитируя их документы):

sql_stmt := 'UPDATE employees SET salary = salary + :1 WHERE ' 
           || v_column || ' = :2';
EXECUTE IMMEDIATE sql_stmt USING amount, column_value; --INJECTION IMMUNE!!

Здесь вы все еще не объединяете входные данные. Вы в безопасности! Ура!

Если ваша база данных не поддерживает что-то подобное выше (надеюсь, что ни один из них все еще не настолько плох, но я не удивлюсь) - или если вам все еще действительно нужно объединить свои данные (как в «иногда» случае представления запросов как Я намекал выше), тогда вы должны использовать правильную функцию выхода. Не пиши это сам. Например, postgres предоставляет функцию quote_literal (). Таким образом, вы бы запустить:

sql_stmt := 'SELECT salary FROM employees WHERE name = ' || quote_literal(in_name);

Таким образом, если in_name является чем-то коварным, как '[snip] или 1 = 1' (часть "или 1 = 1" означает выбрать все строки, позволяя пользователю видеть зарплаты, которые он не должен!), То quote_literal сохраняет ваш зад сделать результирующую строку:

SELECT salary FROM employees WHERE name = '[snip] or 1=1'

Результаты не будут найдены (если у вас нет сотрудников с действительно странными именами.)

В этом суть! Теперь позвольте мне оставить вам ссылку на классическую статью гуру Oracle Тома Кайта на тему SQL-инъекций, чтобы понять суть : Linky

MWDB
источник
Не забудьте упомянуть quote_ident()- но в целом самый простой способ написания динамического SQL, защищенного от инъекций, - это использовать format()и использовать заполнители %Iдля идентификаторов и %Lлитералов. Таким образом, SQL намного более читабелен, чем эквивалентная версия, использующая функции ||и quote_....()функции
a_horse_with_no_name