Внедрение SQL - это очень серьезная проблема безопасности, во многом потому, что ее легко понять неправильно: очевидный, интуитивно понятный способ создания запроса, включающего пользовательский ввод, делает вас уязвимым, а правильный путь для его смягчения требует, чтобы вы знали о параметризации запросы и SQL-инъекция в первую очередь.
Мне кажется, что очевидный способ исправить это - отключить очевидную (но неправильную) опцию: исправить механизм базы данных так, чтобы любой полученный запрос, использующий жестко закодированные значения в своем предложении WHERE вместо параметров, возвращал хороший, описательный сообщение об ошибке, указывающее вам использовать параметры вместо. Очевидно, что для этого потребуется опция отказа, чтобы такие вещи, как специальные запросы от административных инструментов, по-прежнему выполнялись легко, но по умолчанию они должны быть включены.
При таком отключении SQL-инъекция холодная, почти на ночь, но, насколько я знаю, ни одна СУБД на самом деле не делает этого. Есть ли веская причина, почему нет?
bad_ideas_sql = 'SELECT title FROM idea WHERE idea.status == "bad" AND idea.user == :mwheeler'
будет иметь жестко запрограммированные и параметризованные значения в одном запросе - попробуйте поймать это! Я думаю, что есть допустимые варианты использования для таких смешанных запросов.SELECT * FROM jokes WHERE date > DATE_SUB(NOW(), INTERVAL 1 DAY) ORDER BY score DESC;
"bad"
является ли литерал действительно литералом или результатом конкатенации строк. Два решения, которые я вижу, это либо избавление от SQL и других встроенных в строки DSL (да, пожалуйста), либо продвижение языков, в которых конкатенация строк более раздражает, чем использование параметризованных запросов (мм, нет).Ответы:
Слишком много случаев, когда использование литерала является правильным подходом.
С точки зрения производительности, в ваших запросах иногда требуются литералы. Представьте, что у меня есть баг-трекер, где, как только он станет достаточно большим, чтобы беспокоиться о производительности, я ожидаю, что 70% ошибок в системе будут «закрыты», 20% будут «открыты», 5% будут «активными» и 5 % будет в другом статусе. Я могу разумно хотеть иметь запрос, который возвращает все активные ошибки, чтобы быть
вместо передачи в
status
качестве переменной связывания. Я хочу другой план запроса в зависимости от значения, переданного дляstatus
- я хотел бы сделать сканирование таблицы, чтобы вернуть закрытые ошибки и сканирование индекса наstatus
столбец для возврата активных кредитов. Теперь разные базы данных и разные версии имеют разные подходы (более или менее успешно), позволяющие одному и тому же запросу использовать другой план запроса в зависимости от значения переменной bind. Но это имеет тенденцию вносить приличную сложность, которой необходимо управлять, чтобы сбалансировать решение о том, стоит ли повторять разбор запроса или повторно использовать существующий план для нового значения переменной связывания. Для разработчика может иметь смысл иметь дело с этой сложностью. Или может иметь смысл использовать другой путь, когда у меня есть больше информации о том, как будут выглядеть мои данные, чем оптимизатор.С точки зрения сложности кода, есть много раз, когда имеет смысл иметь литералы в операторах SQL. Например, если у вас есть
zip_code
столбец с 5-символьным почтовым индексом и иногда с дополнительными 4 цифрами, имеет смысл сделать что-то вродевместо того, чтобы передавать 4 отдельных параметра для числовых значений. Это не вещи, которые когда-либо будут меняться, поэтому связывание их с переменными только делает код потенциально более трудным для чтения и создает вероятность того, что кто-то будет связывать параметры в неправильном порядке и в результате получит ошибку.
источник
Инъекция SQL происходит, когда запрос строится путем объединения текста из ненадежного и неподтвержденного источника с другими частями запроса. Хотя такое часто случается со строковыми литералами, это не единственный способ, которым это может произойти. Запрос числовых значений может принимать введенную пользователем строку (которая должна содержать только цифры) и объединяться с другим материалом для формирования запроса без кавычек, обычно связанных со строковыми литералами; код, который чрезмерно доверяет проверке на стороне клиента, может иметь такие вещи, как имена полей, взятые из строки запроса HTML. Нет никакого способа, которым код, смотрящий на строку запроса SQL, может видеть, как она была собрана.
Важно не то, содержит ли оператор SQL строковые литералы, а то, содержит ли строка какие-либо последовательности символов из ненадежных источников , и проверка этого будет лучше всего обрабатываться в библиотеке, которая создает запросы. Как правило, в C # нет способа написать код, который будет разрешать строковый литерал, но не будет разрешать другие виды строковых выражений, но можно иметь правило практики кодирования, которое требует, чтобы запросы создавались с использованием класса построения запросов, а не конкатенация строк, и любой, кто передает не-литеральную строку в конструктор запросов, должен оправдать такое действие.
источник
Если вы хотите поместить результаты этих исследований в нижний колонтитул вашего форума, вам нужно будет добавить фиктивный параметр, чтобы каждый раз произносить false. Или наивный веб-программист ищет, как отключить это предупреждение, и затем продолжает.
Теперь вы можете сказать, что добавили бы исключение для перечислений, но это просто снова открывает дыру (хотя и меньше). Не говоря уже о людях, которых нужно сначала обучить, чтобы не использовать
varchars
их.Настоящая проблема внедрения заключается в программном построении строки запроса. Решением для этого является механизм хранимых процедур и принудительное его использование или белый список разрешенных запросов.
источник
deleted = false
наNOT deleted
, что позволяет избежать литерала. Но дело действительно в общем.TL; DR : Вы должны ограничить все литералы, а не только те, которые
WHERE
указаны в пунктах. По причинам, по которым они этого не делают, база данных остается отделенной от других систем.Во-первых, ваша предпосылка ошибочна. Вы хотите ограничить только
WHERE
предложения, но это не единственное место, куда может пойти пользовательский ввод. Например,Это в равной степени уязвимо для внедрения SQL:
Таким образом, вы не можете просто ограничить литералы в
WHERE
предложении. Вы должны ограничить все литералы.Теперь у нас остался вопрос: «Зачем вообще разрешать литералы?» Помните об этом: хотя реляционные базы данных используются под приложением, написанным на другом языке, большой процент времени, не требуется, чтобы вы использовали код приложения для использования базы данных. И здесь у нас есть ответ: для написания кода нужны литералы. Единственной альтернативой может быть требование написания всего кода на каком-либо языке, независимом от базы данных. Таким образом, наличие их дает вам возможность писать «код» (SQL) непосредственно в базе данных. Это ценная развязка, и без литералов это было бы невозможно. (Попробуйте написать на своем любимом языке иногда без литералов. Я уверен, что вы можете себе представить, насколько это будет сложно.)
В качестве общего примера, литералы часто используются в заполнении таблиц списка значений / поиска:
Без них вам нужно было бы написать код на другом языке программирования, чтобы заполнить эту таблицу. Возможность делать это непосредственно в SQL является ценной .
Затем у нас остается еще один вопрос: почему тогда клиентские библиотеки языка программирования не делают этого? И здесь у нас очень простой ответ: им пришлось бы заново реализовать весь анализатор базы данных для каждой поддерживаемой версии базы данных . Зачем? Потому что нет другого способа гарантировать, что вы нашли каждый литерал. Регулярных выражений недостаточно. Например: это содержит 4 отдельных литерала в PostgreSQL:
Попытка сделать это была бы кошмаром обслуживания, тем более что допустимый синтаксис часто меняется между основными выпусками баз данных.
источник