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

142

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

sSanitizedInput = "'" & Replace(sInput, "'", "''") & "'"

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

Мы используем Microsoft SQL Server 2000, для которого, как я считаю, одинарная кавычка является единственным разделителем строк и единственным способом избежать разделителя строк, поэтому нет возможности выполнить что-либо, что вводит пользователь.

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

Что не так с этим кодом? Есть ли способ обойти эту технику очистки при атаке с использованием SQL-инъекции? Был бы очень полезен образец пользовательского ввода, использующего эту технику.


ОБНОВИТЬ:

Я до сих пор не знаю, как эффективно запустить атаку SQL-инъекции против этого кода. Некоторые люди предположили, что обратная косая черта будет экранировать одну одинарную кавычку и оставить другую для завершения строки, чтобы остальная часть строки выполнялась как часть команды SQL, и я понимаю, что этот метод будет работать для внедрения SQL в база данных MySQL, но в SQL Server 2000 единственный способ (который мне удалось найти) избежать одинарной кавычки - это использовать другую одинарную кавычку; обратная косая черта этого не сделает.

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

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

Патрик
источник
18
@BryanH Признание в непонимании того, как общепринятая мудрость применима к конкретному случаю, и просьба привести пример такого конкретного случая - это не высокомерие, а скромность. С другой стороны, раздражение, когда кто-то спрашивает, почему общепринятая мудрость верна, может показаться высокомерным. Рассуждения на конкретных примерах часто являются отличным способом исследовать и учиться. То, как ОП решил это сомнение, было очень полезно для моего понимания предмета, особенно когда он объяснил найденный ответ.
SantiBailors
@patrik Только что наткнулся на это, поскольку я работаю над тем же фрагментом кода, но пытаюсь избежать строки и вложить запрос. Вы когда-нибудь понимали это?
3therk1ll
1
@ 3therk1ll, лучше не пробовать, лучше использовать параметризованный SQL: blog.codinghorror.com/…
Патрик
@ Патрик, я подхожу к этому с точки зрения злоумышленников!
3therk1ll

Ответы:

90

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

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

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

Как это сделать:

  • Проверка белого списка: тип, длина, формат или допустимые значения
  • Если вы хотите внести в черный список, продолжайте. Экранирование кавычек - это хорошо, но в контексте других средств защиты.
  • Используйте объекты Command и Parameter для предварительной обработки и проверки
  • Вызов только параметризованных запросов.
  • А еще лучше использовать исключительно хранимые процедуры.
  • Избегайте использования динамического SQL и не используйте конкатенацию строк для построения запросов.
  • Если вы используете SP, вы также можете ограничить разрешения в базе данных только на выполнение необходимых SP, а не напрямую обращаться к таблицам.
  • вы также можете легко проверить, что вся кодовая база обращается к БД только через SP ...
AviD
источник
2
При правильном использовании динамический SQL и конкатенация строк можно безопасно использовать с параметризованными запросами (т.е. с sp_executesqlвместо EXEC). То есть вы можете динамически генерировать свой оператор SQL, если ни один из составных текстов не исходит от пользователя. Это также дает преимущества в производительности; sp_executesqlподдерживает кеширование.
Брайан
2
@ Брайан, ну да ладно :). Но на самом деле, как часто вы видите, что программисты так поступают? Более того, типичный сценарий, в котором «необходим» динамический SQL, требует ввода данных пользователем как части запроса (предположительно). Если бы вы могли выполнить sp_executesql, вам (обычно) не понадобился бы динамический sql в первую очередь.
AviD
Наконец я столкнулся с ситуацией, которая заставила меня понять, что можно использовать Unicode, чтобы проскользнуть мимо замены строки. Входной текст был введен в Word, который изменил апостроф с прямой версии на «фигурный» апостроф (который больше похож на запятую), на который не повлияла замена строки, но который был обработан SQL как разделитель строк. Сервер. Спасибо за ответ AviD (и всем остальным)!
Патрик
1
@ElRonnoco конечно, но я не сбрасываю со счетов это, так как я видел это в дикой природе больше раз, чем вы думаете ...
AviD
1
@AviD Я обновил ссылку на PDF-файл о контрабанде SQL, который вы написали, до единственной версии, которую я смог найти в Интернете ... сообщите нам, есть ли другое место для вашей статьи.
Майкл Фредриксон
41

Хорошо, этот ответ будет относиться к обновлению вопроса:

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

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

sSanitizedInput = "'" & Заменить (sInput, "'", "''") & "'"

Учтите, что не все они будут действительны всегда и очень зависят от вашего фактического кода вокруг него:

  1. SQL-инъекция второго порядка - если SQL-запрос перестраивается на основе данных, полученных из базы данных после экранирования , данные объединяются без экранирования и могут быть косвенно введены SQL-инъекцией. Увидеть
  2. Усечение строки - (немного сложнее) - сценарий: у вас есть два поля, например имя пользователя и пароль, и SQL объединяет их оба. И оба поля (или только первое) имеют жесткое ограничение по длине. Например, имя пользователя ограничено 20 символами. Скажем, у вас есть этот код:
username = left(Replace(sInput, "'", "''"), 20)

Затем вы получаете имя пользователя, экранированное, а затем обрезанное до 20 символов. Проблема здесь - я вставлю свою цитату в 20-й символ (например, после 19 а), а ваша экранирующая цитата будет обрезана (в 21-м символе). Тогда SQL

sSQL = "select * from USERS where username = '" + username + "'  and password = '" + password + "'"

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

AviD
источник
28

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

Ник Джонсон
источник
2
Что, если вам придется иметь дело с чем-то вроде "таблиц Google Fusion", где, черт возьми, нет никакой доступной библиотеки абстракции, поддерживающей его диалект? Что ты предлагаешь?
systempuntoout
20

Я понимаю, что это прошло много времени после того, как вопрос был задан, но ...

Один из способов начать атаку на процедуру «цитировать аргумент» - это усечение строки. Согласно MSDN, в SQL Server 2000 SP4 (и SQL Server 2005 SP1) слишком длинная строка будет незаметно усечена.

Когда вы заключаете строку в кавычки, она увеличивается в размере. Каждый апостроф повторяется. Затем это можно использовать для выталкивания частей SQL за пределы буфера. Таким образом, вы можете эффективно обрезать части предложения where.

Это, вероятно, было бы в основном полезно в сценарии страницы «администратор пользователя», где вы могли бы злоупотребить оператором «update», чтобы не выполнять все проверки, которые он должен был делать.

Поэтому, если вы решили заключить в кавычки все аргументы, убедитесь, что вы знаете, что происходит с размерами строк, и проследите за тем, чтобы вы не столкнулись с усечением.

Я бы рекомендовал использовать параметры. Всегда. Хотел бы я обеспечить это в базе данных. И как побочный эффект, вы с большей вероятностью получите более качественные обращения к кешу, потому что большинство операторов выглядят одинаково. (Это определенно было верно для Oracle 8)

Йорн Йенсен
источник
1
После публикации я решил, что сообщение AviD освещает это более подробно. Надеюсь, мой пост еще кому-то поможет.
Йорн Йенсен
10

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

Само по себе это безопасно AFAIK. Однако, как заметил другой ответчик, вам также может потребоваться иметь дело с экранированием обратного пространства (хотя, по крайней мере, не при передаче запроса на SQL Server с использованием ADO или ADO.NET - не может ручаться за все базы данных или технологии).

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

Однако в большинстве случаев привязка параметров - это лучший способ - это просто проще.

Pontus Gagge
источник
2
Вы по-прежнему можете использовать подстановку параметров, даже если вы создаете свои собственные запросы.
Ник Джонсон,
1
Вы должны создать строку оператора SQL с нуля, но по-прежнему использовать подстановку параметров.
JeeBee,
Нет, НИКОГДА не создавайте свои операторы SQL с нуля.
AviD
8

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

Том Дитрих
источник
4
И когда вы пропустите этот ОДИН случай на ОДНОМ входе, вы получите pwnd.
BryanH
4
«Некоторые люди, сталкиваясь с проблемой, думают:« Я знаю, я буду использовать регулярные выражения. Теперь у них две проблемы ».
MickeyfAgain_BeforeExitOfSO 05
1
@mickeyf Я знаю, что это распространенное мнение, но, честно говоря, регулярные выражения довольно хороши, если вы их встретите.
Том Дитрих 07
@ tom.dietrich Это всегда зависит от реальной жизненной ситуации. F.ex. Синтаксис regexpr нестандартен, поэтому в целом я бы не советовал использовать regexpr в контекстах, где разные системы интегрированы для совместной работы. Это связано с тем, что разные механизмы regexpr оценивают регулярные выражения по-разному, и, что более важно, этот твердый факт обычно преуменьшается или игнорируется, что может заставить разработчиков не заботиться об этих несовместимости, пока их не укусит. Таких несовместимостей предостаточно; см. например. regular-expressions.info/shorthand.html (ищите на flavorsэтой странице).
SantiBailors
6

В любом случае это плохая идея, как вы, кажется, знаете.

А как насчет экранирования кавычек в такой строке: \ '

Ваша замена приведет к: \ ''

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

WW.
источник
3
Спасибо за ответ! Я знаю, что эта атака сработает для базы данных mySQL, но я почти уверен, что MS SQL Server не примет обратную косую черту в качестве escape-символа (я пробовал). Несколько поисков в Google не выявили никаких других escape-символов, что действительно заставило меня задуматься, почему это не сработает.
Патрик
6

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

Есть способы обойти любой полезный черный список, который вы можете придумать (и некоторые белые списки тоже).

Приличная запись здесь: http://www.owasp.org/index.php/Top_10_2007-A2

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

Неправильный символ
источник
6

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

олле
источник
4

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

Кев
источник
4

Да, это должно работать до тех пор, пока кто-то не запустит SET QUOTED_IDENTIFIER OFF и не использует двойные кавычки для вас.

Изменить: это не так просто, как запретить злоумышленнику отключать цитируемые идентификаторы:

Драйвер ODBC для собственного клиента SQL Server и поставщик OLE DB для собственного клиента SQL Server для SQL Server автоматически устанавливают для QUOTED_IDENTIFIER значение ON при подключении. Это можно настроить в источниках данных ODBC, в атрибутах соединения ODBC или в свойствах соединения OLE DB. По умолчанию SET QUOTED_IDENTIFIER выключен для соединений из приложений DB-Library.

При создании хранимой процедуры параметры SET QUOTED_IDENTIFIER и SET ANSI_NULLS фиксируются и используются для последующих вызовов этой хранимой процедуры .

SET QUOTED_IDENTIFIER также соответствует настройке QUOTED_IDENTIFER для ALTER DATABASE.

SET QUOTED_IDENTIFIER устанавливается во время синтаксического анализа . Установка во время синтаксического анализа означает, что, если инструкция SET присутствует в пакете или хранимой процедуре, она вступает в силу независимо от того, достигает ли выполнение кода этой точки; и инструкция SET вступает в силу до того, как будут выполнены какие-либо инструкции.

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

Марк Брэкетт
источник
1
Это могло бы сработать, но опять же, как они могли заставить этот код выполняться, когда весь пользовательский ввод заключен в одинарные кавычки? Определенная строка (строки) кода, которая могла бы вводить SQL в приведенный выше код, была бы очень полезной. Благодарность!
Патрик
4

Ваша защита не сработает, если:

  • запрос ожидает число, а не строку
  • существовали любые другие способы представления одинарной кавычки, включая:
    • escape-последовательность, например \ 039
    • символ юникода

(в последнем случае это должно быть что-то, что было расширено только после того, как вы сделали свою замену)

AJ.
источник
4

Патрик, вы добавляете одинарные кавычки вокруг ВСЕХ входных данных, даже числовых? Если у вас есть числовой ввод, но вы не заключаете его в одинарные кавычки, то у вас есть доступ.

Роб Крафт
источник
1

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

И зачем изобретать велосипед?

JeeBee
источник
1

Вместо того, чтобы заменять одинарную кавычку (как выглядит) двумя одинарными кавычками, почему бы просто не заменить ее на апостроф, цитату или полностью не удалить?

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

ПРИМЕЧАНИЕ. Ваш метод также предполагает, что все, кто работает с вашим приложением, всегда помнят о необходимости дезинфицировать ввод до того, как он попадет в базу данных, что, вероятно, в большинстве случаев нереально.

Кевин Фэирчайлд
источник
Проголосовали против, поскольку ответ не касается вопроса. Вопрос касается экранирования строк в SQL. Когда вы экранируете произвольную строку (как пытается сделать вопрошающий, чтобы иметь дело с необработанными данными), вы не можете просто заменить проблемные символы произвольными другими; что портит данные. (Кроме того, одинарная цитата ЯВЛЯЕТСЯ апострофом (по крайней мере, в ASCII).)
Эндрюф
-1

Хотя вы можете найти решение, которое работает для строк, для числовых предикатов вам также необходимо убедиться, что они передаются только в числах (простая проверка: можно ли его проанализировать как int / double / decimal?).

Это много лишней работы.

Джозеф Дейгл
источник
-2

Это может сработать, но мне это кажется немного странным. Я бы рекомендовал проверять правильность каждой строки, проверяя ее на регулярное выражение.

Роб
источник
-3

Да, можно, если ...

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

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

    • SQL-операторы проблематичны для проверки
  2. вы либо используете nvarchar/ ncharcolumns (и префикс строковых литералов с N), либо ограничиваете значения, входящие в varchar/ charcolumns только в символы ASCII (например, генерируете исключение при создании оператора SQL)

    • таким образом вы избежите автоматического преобразования апострофа из CHAR (700) в CHAR (39) (и, возможно, других подобных хаков Unicode)
  3. вы всегда проверяете длину значения, чтобы она соответствовала фактической длине столбца (исключение, если оно больше)

    • в SQL Server обнаружен известный дефект, позволяющий обойти ошибку SQL, возникающую при усечении (приводящую к усечению без вывода сообщений)
  4. вы гарантируете, что SET QUOTED_IDENTIFIERвсегдаON

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

Соблюдая эти 4 пункта, вы должны быть в безопасности. Если вы нарушите какой-либо из них, откроется путь для SQL-инъекции.

Miroxlav
источник
1
Это похоже на то, что вы не читали все остальные ответы на этот вопрос 8-летней давности , поскольку любое количество этих ответов указывает на то, что его метод не может остановить инъекцию, если злоумышленник просто использует символы Юникода.
Hogan
@Hogan - Да, но я думаю, что в моем вопросе есть дополнительная ценность. У меня есть большой опыт и проверки того, что я написал. Я знаю, что использовать параметры запроса лучше, но я также полностью понимаю ситуацию, когда кто-то должен избегать этого по разным причинам (например, работодатель требует сохранить старый способ). В этом случае я думаю, что мой ответ очень исчерпывающий и имеет большую ценность, чем ответы, в которых говорится «просто не делайте этого», потому что он указывает путь до конца. Покажите мне здесь другие ответы, которые выглядят так же, и я подумаю об удалении своего.
miroxlav
Хорошо, когда (не если) ваша система будет скомпрометирована, пожалуйста, вернитесь и удалите этот ответ ... или вы можете использовать параметризованный запрос.
Hogan
@Hogan - У меня нет проблем с этим :) Но в настоящее время я утверждаю, что нет известного способа обойти это, если вы соблюдаете 4 правила, которые я опубликовал. Если вы действительно думаете, что есть способ обойти это, просто укажите где.
miroxlav
Плохой совет hombre. любая интерполяция может быть проиграна.
Шейн