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

59

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

  1. Тщательный скрининг, фильтрация, кодирование ввода (перед вставкой в ​​SQL)
  2. Использование подготовленных операторов / параметризованных запросов

Я предполагаю, что у каждого есть свои плюсы и минусы, но почему №2 взлетел и стал более или менее де-факто способом предотвращения инъекционных атак? Это просто безопаснее и менее подвержено ошибкам или были другие факторы?

Как я понимаю, если # 1 используется правильно и все предостережения учтены, это может быть столь же эффективно, как и # 2.

Санитарная обработка, фильтрация и кодирование

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

Параметризованные запросы против библиотеки кодирования

Есть ответы, где понятия parameterized queriesи encoding librariesкоторые трактуются взаимозаменяемо. Поправь меня, если я не прав, но у меня сложилось впечатление, что они разные.

Я понимаю, что encoding libraries, независимо от того , насколько хорошо они всегда имеют потенциал , чтобы изменить SQL «программы», потому что они вносят изменения в сам SQL, прежде чем он будет отправлен в РСУБД.

Parameterized queries с другой стороны, отправьте программу SQL в СУБД, которая затем оптимизирует запрос, определит план выполнения запроса, выберет индексы, которые будут использоваться, и т. д., а затем подключит данные в качестве последнего шага внутри СУБД. сам.

Библиотека кодирования

  data -> (encoding library)
                  |
                  v
SQL -> (SQL + encoded data) -> RDBMS (execution plan defined) -> execute statement

Параметризованный запрос

                                               data
                                                 |
                                                 v
SQL -> RDBMS (query execution plan defined) -> data -> execute statement

Историческое значение

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

Деннис
источник
1
Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
maple_shaft
23
Подготовленные операторы не являются результатом эволюции атак SQL-инъекций. Они были там с самого начала. Ваш вопрос основан на ложной предпосылке.
user207421 14.09.16
4
Если вы думаете, что вы умнее плохих парней, тогда идите на # 1
папараццо
1
«почему PQ остался предпочтительным методом», потому что это самый простой и надежный метод. Плюс вышеупомянутые преимущества производительности для PQ. Там действительно нет недостатков.
Пол Дрейпер
1
Потому что это правильное решение проблемы о том, как выполнять запросы, даже если бы не проблема внедрения SQL в контексте безопасности . Формы, которые требуют экранирования и использования внутриполосных данных с командами, всегда являются ошибкой проектирования, потому что они подвержены ошибкам, не интуитивны и плохо ломаются при неправильном использовании. Смотрите также: сценарии оболочки.
R ..

Ответы:

147

Проблема в том, что # 1 требует от вас эффективного анализа и интерпретации всего варианта SQL, с которым вы работаете, чтобы вы знали, что он делает что-то, чего не должен делать. И обновляйте этот код по мере обновления базы данных. Везде, где вы принимаете ввод для ваших запросов. И не облажайся.

Так что да, такие вещи остановят атаки с использованием SQL-инъекций, но это нелепо дороже в реализации.

Telastyn
источник
60
@dennis - Ну, а какая цитата в твоем варианте SQL? "? '?”? U + 2018? \ U2018? Есть ли уловки для разделения выражений? Могут ли ваши подзапросы обновлять? Есть много вещей, на которые следует обратить внимание.
Теластин,
7
@Dennis каждый движок БД имеет свой собственный способ делать такие вещи, как экранирование символов в строках. Это очень много дыр для подключения, особенно если приложению необходимо работать с несколькими механизмами БД или быть совместимым с будущими версиями того же механизма, что может изменить некоторый незначительный синтаксис запроса, который может быть использован.
12
Еще одним преимуществом подготовленных операторов является выигрыш в производительности, который вы получаете, когда вам приходится повторять один и тот же запрос с разными значениями. Кроме того, подготовленные операторы могут знать, действительно ли значение подразумевается как null, строку или число, и действовать соответственно. Это очень хорошо для безопасности. И даже если вы выполните запрос один раз, ядро ​​БД уже оптимизирует его для вас. Еще лучше, если он кешируется!
Исмаэль Мигель
8
@Dennis Мистер Генри Налл поблагодарит вас за правильную работу.
Матье Гиндон,
14
@ Денис, имя не имеет значения. Проблема с фамилией. См. Переполнение стека , Programmers.SE , Fox Sports , Wired , BBC и все, что вы можете найти в быстром поиске в Google ;-)
Матье Гиндон
80

Потому что вариант 1 не является решением. Экранирование и фильтрация означает отклонение или удаление неверного ввода. Но любой вклад может быть действительным. Например, апостроф является допустимым символом в имени «О'Мэлли». Он просто должен быть правильно закодирован перед использованием в SQL, что и делает подготовленный оператор.


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

JacquesB
источник
2
Вот и все (и это была недостающая часть в двух других ответах, так что +1). Учитывая, как сформулирован вопрос, речь идет не о дезинфекции пользовательского ввода, но я процитирую вопрос: «фильтрация ввода (до вставки)». Если сейчас вопрос заключается в очистке входных данных, то зачем вам делать это самостоятельно, а не позволять библиотеке делать это (в то же время, кстати, теряя возможность кэшировать планы выполнения)?
Арсений Мурзенко
8
@ Денис: дезинфекция или фильтрация означает удаление информации. Кодирование означает преобразование представления данных без потери информации.
JacquesB
9
@Dennis: фильтрация означает либо принятие, либо отклонение ввода пользователя. Например, «Джефф» будет отфильтрован как ввод в поле «Возраст пользователя», потому что значение явно недействительно. Если вместо фильтрации входных данных, вы начинаете преобразуя его, например, заменив одинарную кавычку, то вы делаете именно то же самое, что и библиотеку баз данных , где они используют параметризованные запросы; в этом случае ваш вопрос просто «Зачем мне использовать то, что существует и было написано экспертами в этой области, когда я могу заново изобретать колесо в каждом проекте?»
Арсений Мурзенко
3
@Dennis: O\'Malleyиспользует косую черту, чтобы экранировать кавычки для правильной вставки (по крайней мере, в некоторых базах данных). В MS SQL или Access его можно экранировать дополнительной кавычкой O''Malley. Не очень портативный, если вы должны сделать это самостоятельно.
AbraCadaver
5
Я не могу сказать вам, сколько раз мое имя было отвергнуто системой. Иногда я даже видел ошибки, вызванные SQL-инъекцией только из-за использования моего имени. Черт возьми, меня однажды попросили изменить имя пользователя, потому что я на самом деле что-то ломал в бэкэнде.
Александр О'Мара
60

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

List<Integer> list = /* a list of 1, 2, 3 */
String strList = list.toString();   /* to get "[1, 2, 3]" */
strList = /* manipulate strList to become "[1, 2, 5, 3]" */
list = parseList(strList);

Если бы кто-то предложил это сделать, вы бы правильно ответили, что это довольно смешно, и что нужно просто сделать:

List<Integer> list = /* ... */;
list.add(5, position=2);

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

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

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

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

Здесь есть хорошая аналогия между макросами в стиле C, которые выполняют простую замену текста, и макросами в стиле Lisp, которые генерируют произвольный код. С макросами в стиле C вы можете заменить текст в исходном коде, а это означает, что у вас есть возможность вносить синтаксические ошибки или вводить в заблуждение поведение. С помощью макросов Lisp вы генерируете код в той форме, в которой его обрабатывает компилятор (то есть вы возвращаете фактические структуры данных, которые обрабатывает компилятор, а не текст, который должен обработать читатель, прежде чем компилятор сможет его получить) , С макросом Lisp вы не можете сгенерировать что-то, что могло бы быть ошибкой разбора. Например, вы не можете сгенерировать (let ((ab) a .

Даже с макросами Lisp вы все равно можете генерировать плохой код, потому что вам не обязательно знать о структуре, которая должна быть там. Например, в Лиспе (let ((ab)) a) означает «установить новую лексическую привязку переменной a к значению переменной b, а затем вернуть значение a», а (let (ab) a) означает msgstr "установить новые лексические привязки переменных a и b и инициализировать их обоих как ноль, а затем вернуть значение a." Оба синтаксически правильны, но они означают разные вещи. Чтобы избежать этой проблемы, вы можете использовать более семантически-ориентированные функции и сделать что-то вроде:

Variable a = new Variable("a");
Variable b = new Variable("b");
Let let = new Let();
let.getBindings().add(new LetBinding(a,b));
let.setBody(a);
return let;

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

Джошуа Тейлор
источник
Хорошее объяснение!
Майк Партридж
2
Вы потеряли меня по «хорошей аналогии», но я проголосовал на основании предыдущего объяснения. :)
Уайлдкарт
1
Отличный пример! - И вы можете добавить: в зависимости от типа данных иногда даже невозможно или невозможно создать анализируемую строку. - Что если один из моих параметров - это поле с произвольным текстом, содержащее черновой вариант истории (~ 10.000 символов)? или что если один параметр является JPG-изображением ? - Тогда единственный способ - это параметризованный запрос
Falco
На самом деле нет - это довольно плохое описание того, почему подготовленные операторы развивались как защита от внедрения SQL. В частности, приведенный пример кода написан на языке Java, который отсутствовал при параметризованных запросах, где он, вероятно, разрабатывался в тот период времени, когда C / C ++ считался современным. Базы данных SQL начали использоваться в первые годы 1970-1980 годов. ПУТЬ перед языками более высокого уровня, где популярны. Черт возьми, я бы сказал, что многие из них сделали работу с базами данных проще (кто-нибудь из PowerBuilder?)
TomTom
@ TomTom на самом деле, я согласен с большей частью вашего контента. Я только косвенно коснулся аспекта безопасности здесь. На SO я отвечаю на множество вопросов SPARQL (язык запросов RDF, с некоторыми сходствами с SQL), и многие люди сталкиваются с проблемами, потому что они объединяют строки, а не используют параметризованные запросы. Даже без атак с использованием инъекций параметризованные запросы помогают избежать ошибок / сбоев, а ошибки и сбои также могут быть проблемами безопасности, даже если они не являются инъекционными атаками. Поэтому я бы сказал все меньше и больше: параметризованные запросы хороши, даже если SQL-инъекция не была проблемой, и они хороши ...
Джошуа Тейлор,
21

Помогает то, что вариант № 2 обычно считается наилучшей практикой, поскольку база данных может кэшировать непараметризованную версию запроса. Параметризованные запросы предшествуют проблеме внедрения SQL на несколько лет (я полагаю), так уж сложилось, что вы можете убить двух зайцев одним выстрелом.

JasonB
источник
10
Внедрение SQL было проблемой с момента его изобретения. Это не стало проблемой позже.
Serv
9
@ Сервис Теоретически да. Практически это стало реальной проблемой только тогда, когда наши механизмы ввода были подключены к сети, создавая огромную поверхность для атаки, которую каждый мог взломать.
Ян Догген,
8
Little Bobby Tables не согласится с тем, что для использования SQL-инъекций вам нужен ни интернет, ни большая пользовательская база. И, конечно, сети предшествуют SQL, так что вам не нужно ждать сетей после выхода SQL. Да, уязвимости безопасности менее уязвимы , когда ваше приложение имеет небольшую базу пользователей, но они до сих пор уязвимости в системе безопасности, и люди действительно используют их , когда сама база данных имеют ценные данные (и многие очень рано баз данных были очень ценные данные, так как только люди с ценными базами данных могли позволить себе технологии) ..
Servy
5
@ Насколько мне известно, динамический SQL был относительно поздней функцией; Первоначальное использование SQL в основном было предварительно скомпилировано / предварительно обработано с параметрами для значений (как входящими, так и исходящими), поэтому параметры в запросах могут предшествовать внедрению SQL в программном обеспечении (возможно, не в специальных запросах / запросах CLI).
Марк Роттвил
6
Они могут предшествовать осведомленности о внедрении SQL.
user253751 13.09.16
20

Просто сказал: они этого не сделали. Ваше заявление:

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

в корне ошибочен. Параметризованные запросы существуют намного дольше, чем SQL-инъекция, по крайней мере, широко известна. Как правило, они были разработаны как способ избежать концентрации строк в обычной функциональности «формы для поиска», которую имеют приложения LOB (Line of Business). Многие, МНОГИЕ годы спустя, кто-то обнаружил проблему безопасности при манипуляции с указанными строками.

Я помню, как делал SQL 25 лет назад (когда Интернет НЕ широко использовался - он только начинался), и я помню, что делал SQL против IBM DB5 IIRC версии 5 - и там уже были параметризованные запросы.

TomTom
источник
Благодарю. Почему было необходимо избегать конкатенации строк? Мне кажется, это было бы полезно. У кого-то была проблема с этим?
Деннис
3
Два на самом деле. Во-первых, это не всегда тривиально - зачем заниматься распределением памяти и т. Д., Когда это не нужно. Но, во-вторых, в древние времена производительность кэширования на стороне базы данных sql была не такой уж высокой - компиляция SQL была дорогой. В качестве побочного эффекта от использования одного подготовленного оператора SQL (откуда и берутся параметры), планы проведения могут быть использованы повторно. В SQL Server введена автоматическая параметризация (для повторного использования планов запросов даже без параметров - они вычитаются и подразумеваются). Я думаю, что либо 2000, либо 2007 год - где-то посередине, IIRC.
TomTom
2
Параметризованные запросы не исключают возможность объединения строк. Вы можете выполнить конкатенацию строк для генерации параметризованного запроса. Тот факт, что функция полезна, не означает, что она всегда является хорошим выбором для данной проблемы.
JimmyJames,
Да, но, как я уже сказал, к тому времени, когда они были изобретены, динамический SQL пришел с довольно приличным ударом по производительности;) Даже сегодня люди говорят вам, что планы динамических SQL-запросов на сервере sql не используются повторно (что неправильно, поскольку Я сказал какой-то момент между 2000 и 2007 годами - так довольно долго). В то старое время вы действительно хотели подготовить операторы, если запускаете sql несколько раз;)
TomTom
План кеширования для динамического SQL был фактически добавлен в SQL Server 7.0, в 1998 году - sqlmag.com/database-performance-tuning/…
Майк Диммик,
13

В дополнение ко всем другим хорошим ответам:

Причина, по которой №2 лучше, заключается в том, что он отделяет ваши данные от вашего кода. В № 1 ваши данные являются частью вашего кода, и отсюда все плохие вещи. С # 1 вы получите свой запрос и должны будете выполнить дополнительные шаги, чтобы убедиться, что ваш запрос воспринимает ваши данные как данные, тогда как в # 2 вы получаете свой код и его код, а ваши данные - данные.

Питер Б
источник
3
Разделение кода и данных также означает, что ваша защита от внедрения вредоносного кода написана и протестирована поставщиком базы данных. Поэтому, если что-то, переданное в качестве параметра вместе с безобидным запросом, приводит к разрушению или подрыву вашей базы данных, репутация компании, производящей базу данных, находится на грани, и ваша организация может даже подать в суд на них и выиграть. Это также означает, что если этот код содержит эксплуатируемую ошибку, вполне вероятно, что это чужой сайт, где все чертовски много, а не ваш. (Только не игнорируйте исправления безопасности!)
nigel222
11

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

С точки базы данных SQL зрения select * from employees where last_name = 'Smith'и select * from employees where last_name = 'Fisher'явно отличаются , и поэтому требуют отдельного разбора, компиляции и оптимизации. Они также будут занимать отдельные слоты в области памяти, предназначенной для хранения скомпилированных операторов. В сильно загруженной системе с большим количеством похожих запросов, которые имеют разные параметры вычисления и объем памяти может быть существенным.

Впоследствии использование параметризованных запросов часто дает значительные преимущества в производительности.

mustaccio
источник
Я думаю, что это теория (основанная на используемых подготовленных утверждениях для параметризованных запросов). На практике я сомневаюсь, что это действительно так часто, так как большинство реализаций просто подготавливают-связывают-выполняют за один вызов, поэтому используйте разные подготовленные операторы для каждого параметризованного запроса, если вы не предпримете явных шагов для фактической подготовки операторов (и библиотеки -уровень prepareчасто сильно отличается от реального уровня SQL prepare).
JCaron
Следующие запросы также отличаются от анализатора SQL: SELECT * FROM employees WHERE last_name IN (?, ?)и SELECT * FROM employees WHERE last_name IN (?, ?, ?, ?, ?, ?).
Дамиан Йеррик
Да у них есть. По какой причине MS добавила кэширование плана запросов еще в 1998 году в SQL Server 7. Как в: Ваша информация - поколение старых.
TomTom
1
@TomTom - кэширование плана запроса - это не то же самое, что автоматическая параметризация, на которую вы, похоже, намекаете. Как в, прочитайте, прежде чем отправлять.
Мустаччо
@mustaccio На самом деле, по крайней мере, MS представил оба одновременно.
TomTom
5

Подождите, но почему?

Вариант 1 означает, что вы должны писать процедуры очистки для любого типа ввода, тогда как вариант 2 менее подвержен ошибкам и требует меньше кода для написания / тестирования / обслуживания.

Почти наверняка «забота обо всех предостережениях» может быть более сложной, чем вы думаете, и ваш язык (например, Java PreparedStatement) имеет больше возможностей, чем вы думаете.

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

Тулаинс Кордова
источник
1
JDBC не дезинфицирует anithing. Протокол имеет специфическую часть для параметра, а БД просто не интерпретирует эти параметры. Вот почему вы можете установить имя таблицы из параметра.
Талекс
1
Почему? если параметр не анализируется или не интерпретируется, то нет причин избегать чего-либо.
Талекс
11
Я думаю, у вас неправильное представление о том, как работает параметризованный запрос. Это не просто случай, когда параметры подставляются позже, они никогда не подставляются . СУБД превращает любой запрос в «план», набор шагов, которые он собирается выполнить, чтобы получить ваш результат; в параметризованном запросе этот план похож на функцию: в нем есть ряд переменных, которые необходимо указать при его выполнении. К моменту предоставления переменных строка SQL была полностью забыта, и план просто выполняется с предоставленными значениями.
IMSoP
2
@IMSoP Это было мое неправильное представление. Хотя я думаю, что это общий вопрос, как вы можете видеть в двух самых популярных ответах на этот вопрос в SO stackoverflow.com/questions/3271249/… . Я читал об этом, и вы правы. Я отредактировал ответ.
Тулаинс Кордова
3
@TomTom Это здорово для производительности , но ничего не делает для безопасности . Ко времени компиляции и кэширования скомпрометированного фрагмента динамического SQL программа уже была изменена . Создание плана из нединамического параметризованного SQL и последующая передача элементов данных по-прежнему принципиально отличается от СУБД, абстрагирующейся от сходства между двумя запросами, представленными ему как полные строки SQL.
IMSoP
1

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

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

Так что оставляет кодирование. Вы могли бы начать с функции, которая кодирует строки, добавляя escape-символы, чтобы вы могли заменить их в себе. Поскольку различные базы данных должны различные символы спасаясь (в некоторых базах данных, как \'и ''являются действительными последовательностей для ', но не в других), эта функция должна быть предоставлена поставщиком базы данных.

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

Так что, может быть, было бы проще, если бы база данных также выполняла замещение для вас - она ​​уже знает, какие типы ожидает запрос и как безопасно кодировать данные, и как безопасно подставить их в ваш запрос, так что вам не нужно беспокоиться о это в вашем коде.

На данный момент мы только что заново изобрели параметризованные запросы.

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

Кодирование трудно сделать правильно, а правильное кодирование неотличимо от параметризации.

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

import {sql} from 'cool-sql-library'

let result = sql`select *
    from users
    where user_id = ${user_id}
      and password_hash = ${password_hash}`.execute()

console.log(result)
James_pic
источник
1
«Кодирование сложно сделать правильно» - хахаха. Это не так. Через день или два все это документируется. Я написал кодировщик много лет назад для ORM (потому что у сервера sql есть ограничение на параметры, и поэтому проблематично вставить 5000-10000 строк в одну инструкцию (15 лет назад). Я не помню, чтобы это было большой проблемой.
TomTom
1
Возможно, SQL Server является достаточно регулярным, чтобы не создавать проблем, но я сталкивался с проблемами в других БД - в угловых случаях с несовпадающими кодировками символов, непонятными параметрами конфигурации, специфическими для локали датами и числами. Все решаемо, но нужно хотя бы поверхностное понимание особенностей БД (я смотрю на вас, MySQL и Oracle).
James_pic
3
Кодирование @TomTom на самом деле очень сложно сделать правильно, если учесть время. Что вы делаете, когда ваш поставщик БД решает создать новый стиль комментария в следующем выпуске или когда голое слово становится новым ключевым словом в обновлении? Теоретически вы могли бы получить правильное кодирование для одной версии вашей РСУБД и ошибиться в следующей редакции. Даже не начинайте с того, что происходит, когда вы переключаете поставщиков на тех, у кого есть условные комментарии с использованием нестандартного синтаксиса
Eric
@ Эрик, это откровенно ужасно. (Я использую Postgres; если у него есть такие причудливые бородавки, я еще не встречал их.)
Wildcard
0

В варианте 1 вы работаете с входным набором size = infinity, который вы пытаетесь сопоставить с очень большим выходным размером. В варианте 2 вы ограничили свой ввод тем, что выбрали. Другими словами:

  1. Тщательный скрининг и фильтрация [ бесконечность ] для [ всех безопасных SQL-запросов ]
  2. Использование [ предварительно рассмотренных сценариев, ограниченных вашей областью применения ]

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

Мутант Утконос
источник
0

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

Уязвимость SQL-инъекций изоморфна переполнению буфера в языке, подобном C. История показала, что переполнение буфера чрезвычайно трудно предотвратить - даже очень критический код, подлежащий открытому обзору, часто содержит такие уязвимости.

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

Так что, если уязвимость SQL-инъекции эквивалентна переполнению буфера, то что такое SQL-эквивалент бита NX или страниц памяти только для чтения? Ответ: подготовленные операторы , которые включают в себя параметризованные запросы плюс аналогичные механизмы для запросов, не связанных с запросами. Подготовленный оператор компилируется с определенными частями, помеченными только для чтения, поэтому злоумышленник не может изменить те части программы и другие части, помеченные как неисполняемые данные (параметры подготовленного оператора), в которые злоумышленник может вставить данные, но который никогда не будет рассматриваться как программный код, таким образом устраняя большую часть потенциала для злоупотреблений.

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

Даниэль Приден
источник
Это все мило и модно, но это не относится к вопросу в соответствии с названием.
TomTom
1
@TomTom: Что ты имеешь в виду? Вопрос как раз в том, почему параметризованные запросы являются предпочтительным механизмом предотвращения внедрения SQL; Мой ответ объясняет, почему параметризованные запросы являются более безопасными и надежными, чем дезинфекция пользовательского ввода.
Даниэль Приден
Извините, но МОЙ вопрос гласит: «Почему механизм предотвращения SQL-инъекций развивался в направлении использования параметризованных запросов?». Они не. Дело не в настоящем, а в истории.
TomTom
0

Я уже написал об этом здесь: https://stackoverflow.com/questions/6786034/can-parameterized-statement-stop-all-sql-injection/33033576#33033576

Но для простоты:

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

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

Иосип Ивич
источник
0

Я никогда не использовал SQL. Но очевидно, что вы слышите о проблемах, с которыми сталкиваются люди, и у разработчиков SQL были проблемы с этой «инъекцией SQL». Долгое время я не мог понять это. И тогда я понял, что люди, которые создают операторы SQL, настоящие текстовые операторы SQL, объединяют строки, некоторые из которых были введены пользователем. И моя первая мысль об этом осознании была шоком. Тотальный шок. Я подумала: как кто-нибудь может быть настолько смехотворно глупым и создавать утверждения на любом языке программирования, подобном этому? Для разработчиков на C, C ++, Java или Swift это безумие.

Тем не менее, не очень сложно написать функцию C, которая принимает строку C в качестве аргумента и создает другую строку, которая выглядит точно так же, как строковый литерал в исходном коде C, который представляет ту же строку. Например, эта функция переведет abc в «abc», а «abc» - в «\» abc \ »и« \ abc »в« \ »\\« abc \\ «\» ». (Хорошо, если это выглядит неправильно для вас, это html. Это было правильно, когда я набрал его, но не когда он отображался). И как только эта функция на C написана, совсем не сложно генерировать исходный код на C текст из поля ввода, предоставленного пользователем, превращается в строковый литерал C. Это не сложно сделать безопасным. Почему разработчики SQL не будут использовать этот подход как способ избежать SQL-инъекций, я не знаю.

«Дезинфекция» - абсолютно некорректный подход. Фатальный недостаток в том, что он делает определенные пользовательские вводы незаконными. В итоге вы получите базу данных, в которой универсальное текстовое поле не может содержать такой текст, как; Drop Table или все, что вы использовали бы в SQL-инъекции, чтобы нанести ущерб. Я считаю это совершенно неприемлемым. Если база данных хранит текст, она должна иметь возможность хранить любой текст. И практический недостаток в том, что дезинфицирующее средство не может понять это правильно :-(

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

Таким образом, с точки зрения разработчика, использующего скомпилированные языки, санитарная обработка - это то, что мне никогда не придет в голову. Потребность в дезинфекции безумна. Параметризованные запросы являются очевидным решением проблемы.

(Мне показался интересным ответ Джосипа. Он в основном говорит, что с помощью параметризованных запросов вы можете остановить любую атаку на SQL, но тогда у вас может быть текст в вашей базе данных, который используется для создания инъекции JavaScript :-( Ну, у нас снова та же проблема и я не знаю, есть ли у Javascript решение для этого.

gnasher729
источник
-2

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

Некоторые люди упрощают проблему как «это просто одинарные и двойные кавычки», но хакеры нашли умные способы избежать обнаружения, такие как использование разных кодировок или использование функций базы данных.

В любом случае, вам нужно было забыть только одну строку, чтобы создать катастрофическое нарушение данных. Хакеры могут автоматизировать сценарии для загрузки полной базы данных с сериями или запросами. Если программное обеспечение хорошо известно как пакет с открытым исходным кодом или известный бизнес-пакет, вы можете просто обратиться к таблице пользователей и паролей.

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

Borjab
источник