Может ли параметризованный оператор остановить все инъекции SQL?

Если да, то почему до сих пор так много успешных SQL-инъекций? Просто потому, что некоторые разработчики слишком тупы, чтобы использовать параметризованные операторы?


person iceagle    schedule 22.07.2011    source источник
comment
Это отличный вопрос с абсолютно ужасными ответами (в то время, когда я комментирую)   -  person Ibu    schedule 22.07.2011
comment
Я бы хотел, чтобы кто-то с хорошей репутацией 15k или с хорошим опытом мог внести ценный вклад в этот вопрос.   -  person Ibu    schedule 22.07.2011
comment
См. Билла Карвина Мифы и заблуждения о SQL-инъекциях обсуждение и слайды для получения дополнительной информации по этому вопросу. Он объясняет, что такое SQL-инъекция, почему экранирования обычно недостаточно, и как можно скомпрометировать хранимые процедуры и параметризованные операторы.   -  person Mike    schedule 22.07.2011
comment
Также см. некоторые ответы Билла Карвина на похожие вопросы: Что такое SQL-инъекция?   -  person Mike    schedule 22.07.2011


Ответы (12)


Ссылки, которые я разместил в своих комментариях к вопросу, очень хорошо объясняют проблему. Я резюмировал свои мысли о том, почему проблема не устранена, ниже:

  1. Те, кто только начинает, могут не знать о внедрении SQL.

  2. Некоторые знают о SQL-инъекциях, но думают, что побег — это (единственное?) решение. Если вы выполните быстрый поиск в Google по запросу php mysql query, первая страница, которая появится, будет mysql_query< /a>, на которой есть пример, показывающий интерполяцию экранированного пользовательского ввода в запрос. Там нет упоминания (по крайней мере, я не вижу) использования вместо этого подготовленных операторов. Как уже говорили другие, существует так много учебных пособий, в которых используется интерполяция параметров, что неудивительно, как часто она все еще используется.

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

  4. Другие знают о параметризованных операторах, но не используют их, потому что слышали, что они слишком медленные. Я подозреваю, что многие люди слышали, как невероятно медленны параметризованные операторы, но на самом деле не проводили никаких собственных тестов. Как отметил Билл Карвин в своем выступлении, разницу в производительности редко следует использовать в качестве фактора при рассмотрении вопроса об использовании подготовленных операторов. О преимуществах подготовить один раз, выполнить много раз часто забывают, как и об улучшениях в безопасности и удобстве сопровождения кода.

  5. Некоторые везде используют параметризованные операторы, но с интерполяцией непроверенных значений, таких как имена таблиц и столбцов, ключевые слова и условные операторы. Яркими примерами этого являются динамические поиски, такие как те, которые позволяют пользователям указывать несколько различных полей поиска, условия сравнения и порядок сортировки.

  6. Ложное чувство безопасности при использовании ORM. ORM по-прежнему допускают интерполяцию частей оператора SQL — см. 5.

  7. Программирование — большая и сложная тема, управление базами данных — большая и сложная тема, безопасность — большая и сложная тема. Разработать безопасное приложение базы данных непросто — даже опытные разработчики могут попасться на удочку.

  8. Многие ответы на stackoverflow не помогают. Когда люди пишут вопросы, использующие динамический SQL и интерполяцию параметров, часто не хватает ответов, предлагающих вместо этого использовать параметризованные операторы. В нескольких случаях люди отвергали мое предложение использовать подготовленные операторы — обычно из-за предполагаемых неприемлемых накладных расходов на производительность. Я серьезно сомневаюсь, что те, кто задает большинство этих вопросов, находятся в положении, когда дополнительные несколько миллисекунд, потраченные на подготовку параметризованного оператора, окажут катастрофическое влияние на их приложение.

person Mike    schedule 22.07.2011

Когда в статьях говорится о параметризованных запросах, останавливающих SQL-атаки, они на самом деле не объясняют почему, часто речь идет о том, что «это так, так что не спрашивайте, почему» — возможно, потому, что они сами не знают. Верный признак плохого педагога — это тот, кто не может признать, что чего-то не знает. Но я отвлекся. Когда я говорю, что нашел это вполне понятным, быть сбитым с толку просто. Представьте себе динамический SQL-запрос

sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND Pass=' + password

поэтому простая инъекция sql будет заключаться в том, чтобы просто указать имя пользователя как ' ИЛИ ​​1 = 1 - это эффективно сделает запрос sql:

sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS=' + password

Это говорит о выборе всех клиентов, где их имя пользователя пусто ('') или 1 = 1, что является логическим значением, приравниваемым к истине. Затем он использует -- для комментирования остальной части запроса. Так что это просто распечатает всю таблицу клиентов или сделает с ней все, что вы хотите, если вы войдете в систему, она войдет в систему с привилегиями первого пользователя, который часто может быть администратором.

Теперь параметризованные запросы делают это по-другому, с таким кодом:

sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'

parameters.add("User", username)
parameters.add("Pass", password)

где имя пользователя и пароль являются переменными, указывающими на соответствующие введенные имя пользователя и пароль

В этот момент вы можете подумать, что это вообще ничего не меняет. Конечно, вы все равно могли бы просто ввести в поле имени пользователя что-то вроде Никто ИЛИ 1=1'--, эффективно сделав запрос:

sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND Pass=?'

И это казалось бы веским аргументом. Но вы ошибаетесь.

Принцип работы параметризованных запросов заключается в том, что sqlQuery отправляется как запрос, и база данных точно знает, что этот запрос будет делать, и только тогда она будет вставлять имя пользователя и пароли просто как значения. Это означает, что они не могут повлиять на запрос, потому что база данных уже знает, что будет делать запрос. Таким образом, в этом случае он будет искать имя пользователя «Никто ИЛИ 1 = 1 '--» и пустой пароль, который должен оказаться ложным.

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

person Josip Ivic    schedule 09.10.2015
comment
Это многое добавляет к тому, что я искал, но не могли бы вы подробнее объяснить, что бы вы сделали для проверки ввода? Вы также упомянули, что с запросом могут быть связаны и другие атаки, например, XSS, но не могли бы вы объяснить, как это происходит? Итак, по сути, как мы можем полностью защититься от SQL-инъекций, или мы смотрим на все типы инъекций? благодаря. - person XaolingBao; 27.06.2016
comment
@JosipIvic: Учитывая, сколько людей спрашивали, как работают параметризованные операторы, шокирует то, что так мало - если вообще есть другие - разбивают ответ, как вы. Спасибо, что написали такое понятное объяснение с довольно интуитивным примером. - person daOnlyBG; 14.03.2017
comment
Блестящий. Пример красит тысячу слов, как говорится! - person Drenai; 30.10.2017

Ну хороший вопрос. Ответ скорее стохастический, чем детерминированный, и я попытаюсь объяснить свою точку зрения на небольшом примере.

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

Рассмотрим следующую хранимую процедуру SQL Server, которая получит строку пользователя из таблицы «Пользователи»:

create procedure getUser
 @name varchar(20)
,@pass varchar(20)
as
declare @sql as nvarchar(512)
set @sql = 'select usrID, usrUName, usrFullName, usrRoleID '+
           'from Users '+
           'where usrUName = '''+@name+''' and usrPass = '''+@pass+''''
execute(@sql)

Вы можете получить результаты, передав в качестве параметров имя пользователя и пароль. Предположим, что пароль находится в произвольном тексте (просто для простоты этого примера), обычный вызов будет таким:

DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)

EXECUTE @RC = [dbo].[getUser] 
   @name = 'admin'
  ,@pass = '!@Th1siSTheP@ssw0rd!!'
GO

Но здесь у нас есть плохой метод программирования, используемый программистом внутри хранимой процедуры, поэтому злоумышленник может выполнить следующее:

DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)

EXECUTE @RC = [TestDB].[dbo].[getUser] 
   @name = 'admin'
  ,@pass = 'any'' OR 1=1 --'
GO

Вышеупомянутые параметры будут переданы в качестве аргументов хранимой процедуре, и в конечном итоге будет выполнена команда SQL:

select usrID, usrUName, usrFullName, usrRoleID 
from Users 
where usrUName = 'admin' and usrPass = 'any' OR 1=1 --'

..который вернет все строки от пользователей

Проблема здесь в том, что даже мы следуем принципу "Создать хранимую процедуру и передать поля для поиска в качестве параметров" SQLi все равно выполняется. Это потому, что мы просто копируем нашу плохую практику программирования внутри хранимой процедуры. Решение проблемы состоит в том, чтобы переписать нашу хранимую процедуру следующим образом:

alter procedure getUser
 @name varchar(20)
,@pass varchar(20)
as
select usrID, usrUName, usrFullName, usrRoleID 
from Users 
where usrUName = @name and usrPass = @pass

Я пытаюсь сказать, что разработчики должны сначала узнать, что такое атака SQLi и как ее можно выполнить, а затем соответствующим образом защитить свой код. Слепое следование «лучшим практикам» не всегда является более безопасным способом... и, возможно, именно поэтому у нас так много «лучших практик» — неудач!

person Andreas Venieris    schedule 10.10.2015
comment
Я могу понять вашу точку зрения, и я виновен в этом. Иногда возникает необходимость создания динамического sql-запроса, в котором я использовал конкатенацию параметров. Как бы вы посоветовали мне поступить? - person TheProvost; 15.10.2015
comment
@TheProvost, это хороший вопрос. Рассмотрим sp_executesql: msdn.microsoft.com/en-us/library/ms188001.aspx - person Tim; 15.10.2015
comment
@ Тим Привет, Тим. Я новичок в динамическом sql. В чем разница между sp_executesql и EXECUTE(@SqlQuery) - person TheProvost; 15.10.2015
comment
я думаю, что этот пост хорошо объясняет простой пример: codeproject.com/Tips/586207/ - но в основном EXECUTE(@SqlQuery) ничего не делает для предотвращения внедрения sql, однако sp_executesql(@SqlQuery, ..., ...) предотвращает это. Примеры в статье Microsoft должны помочь. - person Tim; 15.10.2015
comment
У Тима есть решение TheProvost... ;) Вы можете использовать sp_executesql(@QUERY, @PARAMETERS, @VARS)... для случая динамического SQL... ;) - person Andreas Venieris; 16.10.2015

Да, использование подготовленных операторов останавливает все SQL-инъекции, по крайней мере, теоретически. На практике параметризованные операторы могут не быть реальными подготовленными операторами, например. PDO в PHP по умолчанию эмулирует их, поэтому он открыт для атаки в крайних случаях.

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

Если да, то почему до сих пор так много успешных SQL-инъекций? Просто потому, что некоторые разработчики слишком тупы, чтобы использовать параметризованные операторы?

Да, здесь главное образование и унаследованные кодовые базы. Многие учебники используют экранирование, и, к сожалению, их нельзя легко удалить из Интернета.

person kelunik    schedule 09.10.2015
comment
Связанный ответ на самом деле не имеет ничего общего с подготовленными заявлениями. - person Your Common Sense; 09.10.2015
comment
@YourCommonSense: речь идет о параметризованных запросах, они могут быть не фактическими подготовками, а эмулированными в зависимости от используемого драйвера. Это важно знать и очень связано... - person kelunik; 09.10.2015
comment
Другой ответ на той же странице имеет очень хороший комментарий: если ВСЕ ваши запросы параметризованы, вы также защищены от инъекции 2-го порядка. Внедрение 1-го порядка — это забывание о том, что пользовательские данные ненадежны. Внедрение 2-го порядка — это забывание того, что данные базы данных ненадежны (потому что изначально они были получены от пользователя). - person Rodrigo; 09.10.2015
comment
@kelunik связанный ответ также не о параметризованных запросах, а о библиотеке, которая, по сути, их подделывает. Параметризованный запрос — это запрос, который отправляется на сервер с отдельными значениями параметров. - person Panagiotis Kanavos; 15.10.2015
comment
@PanagiotisKanavos: я довольно хорошо знаю содержание этого ответа. Это всего лишь пример (и довольно распространенный), что используемые вами параметризованные запросы на самом деле не могут быть реализованы как подготовленные операторы... - person kelunik; 15.10.2015

Я избегаю абсолютов в программировании; всегда есть исключение. Я настоятельно рекомендую хранимые процедуры и объекты команд. Большая часть моего опыта связана с SQL Server, но время от времени я играю с MySql. У хранимых процедур есть много преимуществ, включая кэшированные планы запросов; да, это можно сделать с помощью параметров и встроенного SQL, но это открывает больше возможностей для инъекционных атак и не помогает разделить задачи. Для меня также намного проще защитить базу данных, поскольку мои приложения обычно имеют разрешение на выполнение только для указанных хранимых процедур. Без прямого доступа к таблице/представлению гораздо сложнее что-либо внедрить. Если пользователь приложения скомпрометирован, у него есть разрешение только на выполнение того, что было заранее определено.

Мои два цента.

person Derek    schedule 14.10.2015
comment
Как это относится к вопросу? Как вы собираетесь вызывать и передавать параметры хранимой процедуре? Используя конкатенацию строк или используя параметризованный запрос? Кроме того, что, если кто-то использует конкатенацию строк внутри хранимой процедуры для создания динамического запроса? То, что это хранимая процедура, не означает, что она безопаснее. - person Panagiotis Kanavos; 15.10.2015
comment
Обычно я использую командный объект, а также избегаю запуска динамических запросов. - person Derek; 19.10.2015

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

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

Чаще разработчики неправильно используют функциональность. Обычно код выглядит примерно так, если все сделано правильно:

db.parameterize_query("select foo from bar where baz = '?'", user_input)

Некоторые разработчики будут объединять строки вместе, а затем использовать параметризованный запрос, который на самом деле не делает вышеупомянутого различия между данными и кодом, что обеспечивает гарантии безопасности, которые мы ищем:

db.parameterize_query("select foo from bar where baz = '" + user_input + "'")

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

person Daniel Crowley    schedule 21.06.2016

Я бы не сказал "тупой".

Я думаю, что учебники являются проблемой. Большинство учебных пособий по SQL, книг и тому подобного объясняют SQL со встроенными значениями, вообще не упоминая параметры привязки. У людей, обучающихся по этим учебникам, нет шансов научиться этому правильно.

person Markus Winand    schedule 22.07.2011
comment
Этого недостаточно. Почему люди не используют фреймворк или какую-то форму? Почему они не проверяют на тупую инъекцию каким-нибудь тупым тестером? Потому что иногда босс плохо платит вам или он платит вам X денег за проект, и вам нужно бегать от проекта к другому и от одного к другому, чтобы получить немного денег. Вы должны быть быстрее и быстрее. Кодировщик напряжен и перегружен, поэтому код работает, но написан плохо. - person jedi; 13.10.2015

Чтобы защитить ваше приложение от SQL-инъекций, выполните следующие действия:

Шаг 1. Ограничьте ввод. Шаг 2. Используйте параметры с хранимыми процедурами. Шаг 3. Используйте параметры с динамическим SQL.

См. http://msdn.microsoft.com/en-us/library/ff648339.aspx

person Fahad Hussain    schedule 22.07.2011
comment
Сами по себе хранимые процедуры на самом деле не помогают. Строки запроса можно создавать динамически в хранимой процедуре, как и в клиентском коде. - person Phil Miller; 22.07.2011
comment
@Fahad Я мог бы перефразировать № 2 как «Использовать параметризованные операторы в запросах и хранимых процедурах». +1 к комментарию Novelocrat о том, что использование хранимых процедур без параметров мало что дает. - person Matthew; 10.09.2016

Поскольку большая часть кода написана без учета безопасности и управления, при наличии выбора между добавлением функций (особенно чего-то видимого, что можно продать) и безопасностью/стабильностью/надежностью (что гораздо труднее продать), они почти всегда выберут то, что нужно. бывший. Безопасность становится проблемой только тогда, когда она становится проблемой.

person evil otto    schedule 22.07.2011

Может ли параметризованный оператор остановить все инъекции SQL?

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

Вот почему я сделал свою оболочку Mysql для PHP, которая поддерживает большинство литералов, которые можно динамически добавлять в запрос, включая массивы и идентификаторы.

Если да, то почему до сих пор так много успешных SQL-инъекций? Просто потому, что некоторые разработчики слишком тупы, чтобы использовать параметризованные операторы?

Как видите, на самом деле просто невозможно параметризовать все ваши запросы, даже если вы не тупой.

person Your Common Sense    schedule 09.10.2015
comment
Если ВСЕ ваши запросы параметризованы (исходят из пользовательских данных или из данных вашей базы данных), то кажется, что вы защищены, как указано в комментарии с наибольшим количеством голосов здесь: stackoverflow.com/a/134138/1086511 - person Rodrigo; 09.10.2015
comment
Я как бы спросил твоего мнения, просто потому, что ты казался достаточно разумным. Я не думаю, что ваш метод будет лучшим, если то, что я прочитал в другом месте, будет верным. В любом случае, я буду рад, если вы улучшите обычные инструменты, которые не могут параметризовать ВСЕ ваши запросы. - person Rodrigo; 10.10.2015
comment
Я начал читать ваш ответ, пока не додумался идентифицировать литералы SQL. Идея показалась мне не совсем правильной (она казалась перегруженной). Если это правда, что параметризованные запросы избегают инъекций в PHP (я все еще изучаю), то мой следующий шаг — избегать инъекций javascript. Затем я вернусь, чтобы изучить ваше решение. Кроме того, я использую postgres, и, возможно, ваше решение специфично для mysql? - person Rodrigo; 10.10.2015
comment
Хорошо, теперь я прочитал это (снова) и не думаю, что параметризация всех ваших запросов просто невозможна, это улучшение. Это невозможно в MySQL? Это невозможно и в PostgreSQL? Почему? Есть ли какой-либо запрос за пределами моего php-скрипта? Где? Я думаю, что под идентификатором вы подразумеваете зарезервированное слово, которое вы пытаетесь удалить из своего массива $_POST? Мне кажется, что это не тот путь (интуитивно я могу ошибаться, конечно). Кроме того, я не понял, вы когда-нибудь пытались его связать? Привязать что? - person Rodrigo; 10.10.2015
comment
Это не так просто найти в сети, как я думал. Если можно, добавьте ссылку. - person Rodrigo; 10.10.2015

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

Я думаю, что проще «просто» написать строку SQL-запроса, соединив несколько разных частей (возможно, даже зависящих от некоторых логических проверок) вместе со значениями, которые нужно вставить. Это просто создание запроса и его выполнение. Еще одним преимуществом является то, что вы можете распечатать (эхо, вывод или что-то еще) строку запроса sql, а затем использовать эту строку для ручного запроса к механизму базы данных.

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

Это немного больше работы (и не так просто программировать), особенно для некоторых «быстрых и грязных» заданий, которые часто оказываются очень долговечными...

С наилучшими пожеланиями,

Коробка

person TomS    schedule 14.10.2015

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

CREATE PROCEDURE show_current_orders
(@name varchar(400) = NULL)
AS
DECLARE @sql nvarchar(4000)
SELECT @sql = ‘SELECT id_num, searchstring FROM searchorders WHERE ‘ +
‘searchstring = ‘’’ + @name + ‘’’’;
EXEC (@sql)
GO

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

person Artinium    schedule 15.08.2018