На мой взгляд, атаки с использованием SQL-инъекций можно предотвратить с помощью:
- Тщательный скрининг, фильтрация, кодирование ввода (перед вставкой в SQL)
- Использование подготовленных операторов / параметризованных запросов
Я предполагаю, что у каждого есть свои плюсы и минусы, но почему №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 требует от вас эффективного анализа и интерпретации всего варианта SQL, с которым вы работаете, чтобы вы знали, что он делает что-то, чего не должен делать. И обновляйте этот код по мере обновления базы данных. Везде, где вы принимаете ввод для ваших запросов. И не облажайся.
Так что да, такие вещи остановят атаки с использованием SQL-инъекций, но это нелепо дороже в реализации.
источник
null
, строку или число, и действовать соответственно. Это очень хорошо для безопасности. И даже если вы выполните запрос один раз, ядро БД уже оптимизирует его для вас. Еще лучше, если он кешируется!Потому что вариант 1 не является решением. Экранирование и фильтрация означает отклонение или удаление неверного ввода. Но любой вклад может быть действительным. Например, апостроф является допустимым символом в имени «О'Мэлли». Он просто должен быть правильно закодирован перед использованием в SQL, что и делает подготовленный оператор.
После того, как вы добавили примечание, кажется, что вы в основном спрашиваете, зачем использовать стандартную библиотечную функцию, а не писать свой собственный функционально похожий код с нуля? Вы всегда должны предпочитать стандартные библиотечные решения написанию собственного кода. Это меньше работы и более ремонтопригодны. Это относится к любой функциональности, но особенно к чему-то, что чувствительно к безопасности, нет абсолютно никакого смысла изобретать велосипед самостоятельно.
источник
O\'Malley
использует косую черту, чтобы экранировать кавычки для правильной вставки (по крайней мере, в некоторых базах данных). В MS SQL или Access его можно экранировать дополнительной кавычкойO''Malley
. Не очень портативный, если вы должны сделать это самостоятельно.Если вы пытаетесь выполнить обработку строк, то вы на самом деле не генерируете SQL-запрос. Вы генерируете строку, которая может генерировать запрос SQL. Есть уровень косвенности, который открывает много места для ошибок и ошибок. Это несколько удивительно, учитывая, что в большинстве случаев мы рады взаимодействовать с чем-то программно. Например, если у нас есть некоторая структура списка и мы хотим добавить элемент, мы обычно этого не делаем:
Если бы кто-то предложил это сделать, вы бы правильно ответили, что это довольно смешно, и что нужно просто сделать:
Это взаимодействует со структурой данных на ее концептуальном уровне. Он не вводит никакой зависимости от того, как эта структура может быть напечатана или проанализирована. Это полностью ортогональные решения.
Ваш первый подход подобен первому примеру (только немного хуже): вы предполагаете, что можете программно построить строку, которая будет правильно проанализирована как запрос, который вы хотите. Это зависит от синтаксического анализатора и целой связки логики обработки строк.
Второй подход с использованием подготовленных запросов во многом похож на второй пример. Когда вы используете подготовленный запрос, вы по сути разбираете псевдопросмотр, который является законным, но содержит некоторые заполнители, а затем используете API для правильной подстановки некоторых значений. Вы больше не задействуете процесс разбора, и вам не нужно беспокоиться о какой-либо обработке строки.
В общем, гораздо проще и гораздо менее подвержено ошибкам взаимодействовать с вещами на их концептуальном уровне. Запрос не является строкой, запрос - это то, что вы получаете, когда разбираете строку или создаете ее программно (или любой другой метод, позволяющий создать ее).
Здесь есть хорошая аналогия между макросами в стиле C, которые выполняют простую замену текста, и макросами в стиле Lisp, которые генерируют произвольный код. С макросами в стиле C вы можете заменить текст в исходном коде, а это означает, что у вас есть возможность вносить синтаксические ошибки или вводить в заблуждение поведение. С помощью макросов Lisp вы генерируете код в той форме, в которой его обрабатывает компилятор (то есть вы возвращаете фактические структуры данных, которые обрабатывает компилятор, а не текст, который должен обработать читатель, прежде чем компилятор сможет его получить) , С макросом Lisp вы не можете сгенерировать что-то, что могло бы быть ошибкой разбора. Например, вы не можете сгенерировать (let ((ab) a .
Даже с макросами Lisp вы все равно можете генерировать плохой код, потому что вам не обязательно знать о структуре, которая должна быть там. Например, в Лиспе (let ((ab)) a) означает «установить новую лексическую привязку переменной a к значению переменной b, а затем вернуть значение a», а (let (ab) a) означает msgstr "установить новые лексические привязки переменных a и b и инициализировать их обоих как ноль, а затем вернуть значение a." Оба синтаксически правильны, но они означают разные вещи. Чтобы избежать этой проблемы, вы можете использовать более семантически-ориентированные функции и сделать что-то вроде:
С чем-то подобным невозможно вернуть что-то, что синтаксически неверно, и гораздо сложнее вернуть что-то случайно не то, что вы хотели.
источник
Помогает то, что вариант № 2 обычно считается наилучшей практикой, поскольку база данных может кэшировать непараметризованную версию запроса. Параметризованные запросы предшествуют проблеме внедрения SQL на несколько лет (я полагаю), так уж сложилось, что вы можете убить двух зайцев одним выстрелом.
источник
Просто сказал: они этого не сделали. Ваше заявление:
в корне ошибочен. Параметризованные запросы существуют намного дольше, чем SQL-инъекция, по крайней мере, широко известна. Как правило, они были разработаны как способ избежать концентрации строк в обычной функциональности «формы для поиска», которую имеют приложения LOB (Line of Business). Многие, МНОГИЕ годы спустя, кто-то обнаружил проблему безопасности при манипуляции с указанными строками.
Я помню, как делал SQL 25 лет назад (когда Интернет НЕ широко использовался - он только начинался), и я помню, что делал SQL против IBM DB5 IIRC версии 5 - и там уже были параметризованные запросы.
источник
В дополнение ко всем другим хорошим ответам:
Причина, по которой №2 лучше, заключается в том, что он отделяет ваши данные от вашего кода. В № 1 ваши данные являются частью вашего кода, и отсюда все плохие вещи. С # 1 вы получите свой запрос и должны будете выполнить дополнительные шаги, чтобы убедиться, что ваш запрос воспринимает ваши данные как данные, тогда как в # 2 вы получаете свой код и его код, а ваши данные - данные.
источник
Параметризованные запросы, помимо обеспечения защиты от внедрения SQL, часто имеют дополнительное преимущество: они компилируются только один раз, а затем выполняются несколько раз с разными параметрами.
С точки базы данных SQL зрения
select * from employees where last_name = 'Smith'
иselect * from employees where last_name = 'Fisher'
явно отличаются , и поэтому требуют отдельного разбора, компиляции и оптимизации. Они также будут занимать отдельные слоты в области памяти, предназначенной для хранения скомпилированных операторов. В сильно загруженной системе с большим количеством похожих запросов, которые имеют разные параметры вычисления и объем памяти может быть существенным.Впоследствии использование параметризованных запросов часто дает значительные преимущества в производительности.
источник
prepare
часто сильно отличается от реального уровня SQLprepare
).SELECT * FROM employees WHERE last_name IN (?, ?)
иSELECT * FROM employees WHERE last_name IN (?, ?, ?, ?, ?, ?)
.Подождите, но почему?
Вариант 1 означает, что вы должны писать процедуры очистки для любого типа ввода, тогда как вариант 2 менее подвержен ошибкам и требует меньше кода для написания / тестирования / обслуживания.
Почти наверняка «забота обо всех предостережениях» может быть более сложной, чем вы думаете, и ваш язык (например, Java PreparedStatement) имеет больше возможностей, чем вы думаете.
Подготовленные операторы или параметризованные запросы предварительно компилируются на сервере базы данных, поэтому при задании параметров конкатенация SQL не выполняется, поскольку запрос больше не является строкой SQL. Дополнительным преимуществом является то, что СУБД кэширует запрос, и последующие вызовы рассматриваются как один и тот же SQL, даже если значения параметров различаются, тогда как при сцепленном SQL каждый раз, когда запрос выполняется с разными значениями, запрос отличается, и СУБД должна его проанализировать. создайте план выполнения снова и т. д.
источник
Давайте представим, как будет выглядеть идеальный подход «дезинфекции, фильтрации и кодирования».
Санитарная обработка и фильтрация могут иметь смысл в контексте конкретного приложения, но в конечном итоге они оба сводятся к тому, что «вы не можете поместить эти данные в базу данных». Для вашего приложения это может быть хорошей идеей, но это не то, что вы можете рекомендовать в качестве общего решения, так как будут приложения, которые должны иметь возможность хранить произвольные символы в базе данных.
Так что оставляет кодирование. Вы могли бы начать с функции, которая кодирует строки, добавляя escape-символы, чтобы вы могли заменить их в себе. Поскольку различные базы данных должны различные символы спасаясь (в некоторых базах данных, как
\'
и''
являются действительными последовательностей для'
, но не в других), эта функция должна быть предоставлена поставщиком базы данных.Но не все переменные являются строками. Иногда вам нужно подставить целое число или дату. Они представлены по-разному в строках, поэтому вам нужны разные методы кодирования (опять же, они должны быть специфичными для поставщика базы данных), и вам необходимо подставить их в запрос различными способами.
Так что, может быть, было бы проще, если бы база данных также выполняла замещение для вас - она уже знает, какие типы ожидает запрос и как безопасно кодировать данные, и как безопасно подставить их в ваш запрос, так что вам не нужно беспокоиться о это в вашем коде.
На данный момент мы только что заново изобрели параметризованные запросы.
А после параметризации запросов это открывает новые возможности, такие как оптимизация производительности и упрощенный мониторинг.
Кодирование трудно сделать правильно, а правильное кодирование неотличимо от параметризации.
Если вы действительно любите интерполяции строк как способ построения запросов, есть несколько языков (Scala и ES2015 приходят на ум) , которые имеют подключаемую интерполяцию строки, поэтому там есть библиотеки , которые позволяют писать параметризованных запросов , которые выглядят как строки интерполяции, но защищены от внедрения SQL-кода, поэтому в синтаксисе ES2015:
источник
В варианте 1 вы работаете с входным набором size = infinity, который вы пытаетесь сопоставить с очень большим выходным размером. В варианте 2 вы ограничили свой ввод тем, что выбрали. Другими словами:
Согласно другим ответам, также есть некоторые преимущества в производительности от ограничения вашей области от бесконечности до чего-то управляемого.
источник
Одна полезная ментальная модель SQL (особенно современных диалектов) состоит в том, что каждый оператор или запрос SQL является программой. В нативной двоичной исполняемой программе наиболее опасными видами уязвимостей являются переполнения, когда злоумышленник может перезаписать или изменить программный код с помощью различных инструкций.
Уязвимость SQL-инъекций изоморфна переполнению буфера в языке, подобном C. История показала, что переполнение буфера чрезвычайно трудно предотвратить - даже очень критический код, подлежащий открытому обзору, часто содержит такие уязвимости.
Одним из важных аспектов современного подхода к решению уязвимостей, связанных с переполнением, является использование аппаратных средств и механизмов ОС для маркировки определенных частей памяти как неисполняемых, а также для обозначения других частей памяти только для чтения. (См., Например, статью Википедии о защите исполняемого пространства .) Таким образом, даже если злоумышленник может изменить данные, злоумышленник не может заставить свои введенные данные обрабатываться как код.
Так что, если уязвимость SQL-инъекции эквивалентна переполнению буфера, то что такое SQL-эквивалент бита NX или страниц памяти только для чтения? Ответ: подготовленные операторы , которые включают в себя параметризованные запросы плюс аналогичные механизмы для запросов, не связанных с запросами. Подготовленный оператор компилируется с определенными частями, помеченными только для чтения, поэтому злоумышленник не может изменить те части программы и другие части, помеченные как неисполняемые данные (параметры подготовленного оператора), в которые злоумышленник может вставить данные, но который никогда не будет рассматриваться как программный код, таким образом устраняя большую часть потенциала для злоупотреблений.
Конечно, дезинфекция пользовательского ввода - это хорошо, но чтобы действительно быть в безопасности, нужно быть параноиком (или, что то же самое, думать как злоумышленник). Поверхность управления вне текста программы - способ сделать это, и подготовленные операторы предоставляют эту поверхность управления для SQL. Поэтому неудивительно, что подготовленные операторы и, следовательно, параметризованные запросы являются подходом, который рекомендует подавляющее большинство специалистов по безопасности.
источник
Я уже написал об этом здесь: https://stackoverflow.com/questions/6786034/can-parameterized-statement-stop-all-sql-injection/33033576#33033576
Но для простоты:
Параметризованные запросы работают так, что sqlQuery отправляется как запрос, и база данных точно знает, что будет делать этот запрос, и только тогда она будет вводить имя пользователя и пароли просто в качестве значений. Это означает, что они не могут повлиять на запрос, потому что база данных уже знает, что будет делать запрос. Таким образом, в этом случае он будет искать имя пользователя «Никто ИЛИ 1 = 1» - и пустой пароль, который должен выдаваться ложным.
Это не полное решение, и проверка входных данных все еще должна быть выполнена, поскольку это не повлияет на другие проблемы, такие как атаки XSS, так как вы все равно можете поместить javascript в базу данных. Затем, если это будет считано на странице, он будет отображаться как обычный javascript, в зависимости от проверки вывода. Поэтому лучше всего по-прежнему использовать проверку ввода, но использовать параметризованные запросы или хранимые процедуры, чтобы остановить любые атаки SQL
источник
Я никогда не использовал 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 решение для этого.
источник
Основная проблема заключается в том, что хакеры нашли способы окружить санитарию, в то время как параметризованные запросы были существующей процедурой, которая прекрасно работала с дополнительными преимуществами производительности и памяти.
Некоторые люди упрощают проблему как «это просто одинарные и двойные кавычки», но хакеры нашли умные способы избежать обнаружения, такие как использование разных кодировок или использование функций базы данных.
В любом случае, вам нужно было забыть только одну строку, чтобы создать катастрофическое нарушение данных. Хакеры могут автоматизировать сценарии для загрузки полной базы данных с сериями или запросами. Если программное обеспечение хорошо известно как пакет с открытым исходным кодом или известный бизнес-пакет, вы можете просто обратиться к таблице пользователей и паролей.
С другой стороны, просто использование составных запросов было просто вопросом обучения и привыкания к нему.
источник