Я понимаю, что параметризованные SQL-запросы - это оптимальный способ дезинфекции пользовательского ввода при построении запросов, содержащих пользовательский ввод, но мне интересно, что не так с вводом пользователя и экранированием одинарных кавычек и окружением всей строки одинарными кавычками. Вот код:
sSanitizedInput = "'" & Replace(sInput, "'", "''") & "'"
Любая одинарная кавычка, которую вводит пользователь, заменяется двойными одинарными кавычками, что лишает пользователей возможности заканчивать строку, поэтому все, что они могут ввести, например, точки с запятой, знаки процента и т. д., Все будет частью строки и фактически не выполняется как часть команды.
Мы используем Microsoft SQL Server 2000, для которого, я считаю, одинарная кавычка является единственным разделителем строк и единственным способом избежать разделителя строк, поэтому нет возможности выполнить что-либо, что вводит пользователь.
Я не вижу никакого способа противодействовать этому с помощью SQL-инъекции, но я понимаю, что если бы это было столь же пуленепробиваемым, как мне кажется, кто-то другой уже подумал бы об этом, и это было бы обычной практикой.
Что не так с этим кодом? Есть ли способ обойти эту технику очистки при атаке с использованием SQL-инъекции? Был бы очень полезен образец пользовательского ввода, использующего эту технику.
ОБНОВИТЬ:
Я до сих пор не знаю, как эффективно запустить атаку SQL-инъекции против этого кода. Некоторые люди предположили, что обратная косая черта будет экранировать одну одинарную кавычку и оставить другую, чтобы завершить строку, чтобы остальная часть строки выполнялась как часть команды SQL, и я понимаю, что этот метод будет работать для вставки SQL в база данных MySQL, но в SQL Server 2000 единственный способ (который мне удалось найти) избежать одинарной кавычки - это использовать другую одинарную кавычку; обратная косая черта этого не сделает.
И если нет способа остановить экранирование одинарной кавычки, ни один из остальных вводимых пользователем данных не будет выполнен, потому что все они будут приняты как одна непрерывная строка.
Я понимаю, что есть более эффективные способы дезинфекции ввода, но мне действительно больше интересно узнать, почему метод, который я предоставил выше, не работает. Если кто-то знает какой-либо конкретный способ организовать атаку SQL-инъекции против этого метода очистки, я бы с удовольствием его увидел.
@patrik Просто наткнулся на это, поскольку я работаю над тем же фрагментом кода, но пытаюсь избежать строки и вложить запрос. Вы когда-нибудь понимали это?
@ 3therk1ll лучше не пробовать, лучше использовать параметризованный SQL: blog.codinghorror.com/…
@ Патрик, я подхожу к этому с точки зрения злоумышленников!


Это может сработать, но мне это кажется немного странным. Я бы рекомендовал проверять правильность каждой строки, проверяя ее на соответствие регулярному выражению.
Хотя вы можете найти решение, которое работает для строк, для числовых предикатов вам также необходимо убедиться, что они передаются только в числах (простая проверка: можно ли его проанализировать как int / double / decimal?).
Это много лишней работы.
В любом случае это плохая идея, как вы, кажется, знаете.
А как насчет экранирования кавычек в такой строке: \ '
Ваша замена приведет к: \ ''
Если обратная косая черта выходит за пределы первой кавычки, значит, вторая кавычка заканчивает строку.
Спасибо за ответ! Я знаю, что эта атака сработает для базы данных mySQL, но я почти уверен, что MS SQL Server не примет обратную косую черту в качестве escape-символа (я пробовал). Несколько поисков в Google не выявили никаких других escape-символов, что действительно заставило меня задуматься, почему это не сработает.
Каким уродливым кодом была бы вся эта дезинфекция пользовательского ввода! Затем неуклюжий StringBuilder для оператора SQL. Подготовленный метод оператора приводит к гораздо более чистому коду, а преимущества SQL Injection - действительно хорошее дополнение.
И зачем изобретать велосипед?
Вместо того, чтобы заменять одинарную кавычку (как выглядит) двумя одинарными кавычками, почему бы просто не заменить ее на апостроф, цитату или полностью не удалить?
В любом случае, это немного путаница ... особенно когда у вас есть законные вещи (например, имена), в которых могут использоваться одинарные кавычки ...
ПРИМЕЧАНИЕ. Ваш метод также предполагает, что все, кто работает с вашим приложением, всегда помнят о необходимости дезинфицировать ввод до того, как он попадет в базу данных, что, вероятно, в большинстве случаев нереально.
Проголосовано против, поскольку ответ не касается вопроса. Вопрос касается экранирования строк в SQL. Когда вы экранируете произвольную строку (как пытается сделать вопрошающий, чтобы иметь дело с необработанными данными), вы не можете просто заменить проблемные символы произвольными другими; что портит данные. (Кроме того, одинарная кавычка ЯВЛЯЕТСЯ апострофом (по крайней мере, в ASCII).)
Если у вас есть параметризованные запросы, вы должны использовать их всегда. Достаточно одного запроса, чтобы проскользнуть через сеть, и ваша БД окажется под угрозой.
Входная санитария - это не то, что вам нужно наполовину. Используй всю свою задницу. Используйте регулярные выражения в текстовых полях. Попробуйте преобразовать числа в правильный числовой тип и сообщить об ошибке проверки, если это не сработает. Очень легко искать шаблоны атак во входных данных, таких как '-. Предположим, что весь ввод от пользователя враждебен.
И когда вы пропустите этот случай ОДИН на входе ОДИН, вы pwnd.
«Некоторые люди, столкнувшись с проблемой, думают:« Я знаю, я буду использовать регулярные выражения. Теперь у них две проблемы ».
@mickeyf Я знаю, что это распространенное мнение, но, честно говоря, регулярные выражения довольно хороши, если вы их встретите.
@ tom.dietrich Это всегда зависит от реальной жизненной ситуации. F.ex. Синтаксис regexpr нестандартен, поэтому в целом я бы не советовал использовать regexpr в контекстах, где разные системы интегрированы для совместной работы. Это связано с тем, что разные механизмы regexpr оценивают регулярные выражения по-разному, и, что более важно, этот твердый факт обычно преуменьшается или игнорируется, что может заставить разработчиков не заботиться об этих несовместимости, пока их не укусит. Таких несовместимостей предостаточно; см. например. regular-expressions.info/shorthand.html (найдите flavors на этой странице).
В двух словах: никогда не выполняйте экранирование запросов. Вы обязательно ошибетесь. Вместо этого используйте параметризованные запросы или, если по какой-то причине вы не можете этого сделать, используйте существующую библиотеку, которая сделает это за вас. Нет причин делать это самому.
Что, если вам придется иметь дело с чем-то вроде "таблиц Google Fusion", где, кстати, нет какой-либо доступной библиотеки абстракций, поддерживающей его диалект? Что ты предлагаешь?
Вопрос не в том, что мудрее, а в том, как именно то или иное решение на самом деле терпит неудачу. Если вы не знаете, значит, у вас нет ответа на этот вопрос.
Я использовал эту технику при работе с функцией «расширенного поиска», когда создание запроса с нуля было единственным жизнеспособным ответом. (Пример: разрешить пользователю искать продукты на основе неограниченного набора ограничений на атрибуты продуктов, отображая столбцы и их разрешенные значения в качестве элементов управления графическим интерфейсом, чтобы снизить порог обучения для пользователей.)
Само по себе это безопасно AFAIK. Однако, как указал другой ответчик, вам также может потребоваться иметь дело с экранированием обратного пространства (хотя, по крайней мере, не при передаче запроса на SQL Server с использованием ADO или ADO.NET - не может ручаться за все базы данных или технологии).
Загвоздка в том, что вы действительно должны быть уверены, какие строки содержат вводимые пользователем данные (всегда потенциально вредоносные) и какие строки являются допустимыми SQL-запросами. Одна из ловушек заключается в том, что если вы используете значения из базы данных - были ли эти значения изначально предоставлены пользователем? Если это так, их также необходимо избежать. Мой ответ - попытаться провести дезинфекцию как можно позже (но не позже!) При построении SQL-запроса.
Однако в большинстве случаев привязка параметров - это лучший способ - это просто проще.
Вы по-прежнему можете использовать подстановку параметров, даже если вы создаете свои собственные запросы.
Вы должны создать строку оператора SQL с нуля, но по-прежнему использовать подстановку параметров.
Нет, НИКОГДА не создавайте свои операторы SQL с нуля.
Во-первых, это просто плохая практика. Проверка ввода необходима всегда, но она также всегда сомнительна. Что еще хуже, проверка черного списка всегда проблематична, гораздо лучше явно и строго определить, какие значения / форматы вы принимаете. По общему признанию, это не всегда возможно, но до некоторой степени это необходимо делать всегда. Некоторые исследовательские работы по теме:
Дело в том, что любой черный список (и слишком разрешительные белые списки) можно обойти. Последняя ссылка на мою статью показывает ситуации, когда можно обойти даже экранирование кавычек.
Даже если эти ситуации к вам не относятся, это все равно плохая идея. Более того, если ваше приложение не является тривиально маленьким, вам придется иметь дело с обслуживанием и, возможно, с определенной степенью управления: как вы обеспечить, чтобы оно выполнялось правильно, везде и в любое время?
Как это сделать:
При правильном использовании динамический SQL и конкатенация строк можно безопасно использовать с параметризованными запросами (то есть с sp_executesql вместо EXEC). То есть вы можете динамически генерировать свой оператор SQL, если ни один из составных текстов не исходит от пользователя. Это также дает преимущества в производительности; sp_executesql поддерживает кеширование.
@ Брайан, ну да ладно :). Но на самом деле, как часто вы видите, что программисты так поступают? Более того, в типичном сценарии, где «нужен» динамический SQL, требует пользовательский ввод как часть запроса (предположительно). Если бы вы могли выполнить sp_executesql, вам (обычно) вообще не понадобился бы динамический sql.
В конце концов я столкнулся с ситуацией, которая заставила меня понять, что можно использовать Unicode, чтобы проскользнуть мимо замены строки. Вводимый текст был введен в Word, который изменил апостроф с прямой версии на «фигурный» апостроф (который больше похож на запятую), на который не повлияла замена строки, но который был обработан SQL как разделитель строк. Сервер. Спасибо за ответ AviD (и всем остальным)!
@AviD Я думаю, что Брайан говорит о том, что текст SqlCommand не содержит ввода непосредственно от пользователя. Вместо этого он будет содержать заполнители параметров, такие как @name, которые затем можно установить на все, что угодно, без риска инъекции. Я не считаю, что кто-то создаст что-то тупое вроде SqlCommand.Text = "EXEC(@userinput)", которое, очевидно, будет уничтожено заслуживать.
@ElRonnoco конечно, но я не скидку на это, так как я видел это в дикой природе больше раз, чем вы думаете ...
Ссылка на PDF-файл «Контрабанда SQL» теперь не работает. Можно ссылку обновить?
@AviD Я обновил ссылку на PDF-файл о контрабанде SQL, который вы написали, до единственной версии, которую я смог найти в Интернете ... сообщите нам, есть ли другое место для вашей статьи.
Спасибо, @MichaelFredrickson!
«Вызов только параметризованных запросов»: да ... пока вы не столкнетесь с JSON_MODIFY, который ТОЛЬКО принимает строковые литералы в качестве пути. Facedesk ...
Вероятно, стоит отметить, что, как правило, разумно использовать ORM, если производительность не является основной проблемой, просто убедитесь, что он хороший (т. Е. Не используйте шаблоны Active Record, используйте сопоставители данных и, ради бога, изучите и используйте базу данных боковые ограничения). Причиной этого обычно является то, что люди, которые пишут ORM, много более опытны с безопасностью SQL и, вероятно, будут получать уведомления об уязвимостях и действовать в соответствии с ними. Многие из них имеют встроенные достойные фреймворки для валидации, а также вечно полезные системы миграции.
Простой ответ: иногда это срабатывает, но не всегда. Вы хотите использовать проверку белого списка для все, но я понимаю, что это не всегда возможно, поэтому вы вынуждены использовать черный список с наилучшими предположениями. Точно так же вы хотите использовать параметризованные хранимые процедуры в все, но, опять же, это не всегда возможно, поэтому вы вынуждены использовать sp_execute с параметрами.
Есть способы обойти любой полезный черный список, который вы можете придумать (и некоторые белые списки тоже).
Приличная рецензия здесь: http://www.owasp.org/index.php/Top_10_2007-A2
Если вам нужно сделать это как быстрое исправление, чтобы дать вам время установить настоящий, сделайте это. Но не думайте, что вы в безопасности.
Это можно сделать двумя способами, без исключений, чтобы обезопасить себя от SQL-инъекций; подготовленные операторы или параметризованные хранимые процедуры.
Да, это должно работать до тех пор, пока кто-нибудь не запустит ВЫКЛЮЧИТЬ QUOTED_IDENTIFIER и не использует двойные кавычки для вас.
Обновлено: это не так просто, как запретить злоумышленнику отключать цитируемые идентификаторы:
The SQL Server Native Client ODBC driver and SQL Server Native Client OLE DB Provider for SQL Server automatically set QUOTED_IDENTIFIER to ON when connecting. This can be configured in ODBC data sources, in ODBC connection attributes, or OLE DB connection properties. The default for SET QUOTED_IDENTIFIER is OFF for connections from DB-Library applications.
When a stored procedure is created, the SET QUOTED_IDENTIFIER and SET ANSI_NULLS settings are captured and used for subsequent invocations of that stored procedure.
SET QUOTED_IDENTIFIER also corresponds to the QUOTED_IDENTIFER setting of ALTER DATABASE.
SET QUOTED_IDENTIFIER is set at parse time. Setting at parse time means that if the SET statement is present in the batch or stored procedure, it takes effect, regardless of whether code execution actually reaches that point; and the SET statement takes effect before any statements are executed.
Существует множество способов отключения QUOTED_IDENTIFIER без вашего ведома. По общему признанию - это не та самая уязвимость, которую вы ищете, но это довольно большая поверхность для атаки. Конечно, если вы также избежали двойных кавычек - тогда мы вернулись к тому, с чего начали. ;)
Это могло бы сработать, но опять же, как они могли заставить этот код выполняться, когда весь пользовательский ввод заключен в одинарные кавычки? Была бы очень полезна конкретная строка (и) кода, которая могла бы вводить SQL в приведенный выше код. Спасибо!
Ваша защита не сработает, если:
(в последнем случае это должно быть что-то, что было расширено только после того, как вы сделали свою замену)
Патрик, вы добавляете одинарные кавычки вокруг ВСЕХ входных данных, даже числовых? Если у вас есть числовой ввод, но вы не заключаете его в одинарные кавычки, то у вас есть доступ.
Хорошо, этот ответ будет относиться к обновлению вопроса:
"If anyone knows of any specific way to mount a SQL injection attack against this sanitization method I would love to see it."
Теперь, помимо экранирования обратной косой черты MySQL - и с учетом того, что мы на самом деле говорим о MSSQL, на самом деле существует 3 возможных способа SQL-инъекции вашего кода.
sSanitizedInput = "'" & Replace(sInput, "'", "''") & "'"
Учтите, что не все они будут действительны всегда и очень зависят от вашего фактического кода вокруг него:
username = left(Replace(sInput, "'", "''"), 20)
Затем вы получаете имя пользователя, экранированное, а затем обрезанное до 20 символов. Проблема здесь - я вставлю свою цитату в 20-й символ (например, после 19 а), и ваша экранирующая цитата будет обрезана (в 21-м символе). Тогда SQL
sSQL = "select * from USERS where username = '" + username + "' and password = '" + password + "'"
в сочетании с вышеупомянутым искаженным именем пользователя приведет к тому, что пароль уже будет вне кавычки, и будет просто содержать полезную нагрузку напрямую. 3. Контрабанда Unicode - В определенных ситуациях можно передать высокоуровневый символ Unicode, который выглядит похож на кавычку, но не - до тех пор, пока он не попадет в базу данных, где вдруг это. Поскольку это не цитата, когда вы ее проверяете, все пройдет легко ... Подробнее см. В моем предыдущем ответе и ссылку на исходное исследование.
Я понимаю, что это прошло много времени после того, как вопрос был задан, но ...
Один из способов начать атаку на процедуру «цитировать аргумент» - это усечение строки. Согласно MSDN, в SQL Server 2000 SP4 (и SQL Server 2005 SP1) слишком длинная строка будет незаметно усечена.
Когда вы заключаете строку в кавычки, она увеличивается в размере. Каждый апостроф повторяется. Затем это можно использовать для выталкивания частей SQL за пределы буфера. Таким образом, вы можете эффективно обрезать части предложения where.
Это, вероятно, было бы в основном полезно в сценарии страницы «администратор пользователя», где вы могли бы злоупотребить оператором «update», чтобы не выполнять все проверки, которые он должен был делать.
Поэтому, если вы решите заключить в кавычки все аргументы, убедитесь, что вы знаете, что происходит с размерами строк, и проследите, чтобы не произошло усечения.
Я бы порекомендовал пойти с параметрами. Всегда. Просто хочу, чтобы я мог обеспечить это в базе данных. И, как побочный эффект, вы с большей вероятностью получите лучшее попадание в кеш, потому что большинство операторов выглядят одинаково. (Это действительно было верно в Oracle 8)
После публикации я решил, что сообщение AviD освещает это более подробно. Надеюсь, мой пост еще кому-то поможет.
После изучения темы я считаю, что обработка вводимых данных, как вы предложили, безопасна, но только при соблюдении следующих правил:
вы никогда не позволяете строковым значениям, поступающим от пользователей, становиться ничем иным, кроме строковых литералов (т. е. избегайте указания параметра конфигурации: «Введите сюда дополнительные имена / выражения столбцов SQL:»). Типы значений, отличные от строк (числа, даты, ...): преобразуйте их в их собственные типы данных и предоставьте процедуру для литерала SQL из каждого типа данных.
вы либо используете столбцы nvarchar / nchar (и строковые литералы префикса с N), либо предельные значения, входящие в столбцы varchar / char, только для символов ASCII (например, исключение исключения при создании оператора SQL)
вы всегда проверяете длину значения, чтобы она соответствовала фактической длине столбца (исключение, если оно больше)
вы гарантируете, что SET QUOTED_IDENTIFIER всегда будет ON
Соблюдая эти 4 пункта, вы должны быть в безопасности. Если вы нарушите какой-либо из них, откроется путь для SQL-инъекции.
Это похоже на то, что вы не читали все остальные ответы на этот 8-летний вопрос, поскольку любое количество этих ответов указывает на его метод не может остановить инъекцию, если злоумышленник просто использует символы Unicode.
@Hogan - Да, но я думаю, что в моем вопросе есть дополнительная ценность. У меня есть большой опыт и тестирование того, что я написал. Я знаю, что использовать параметры запроса лучше, но я также полностью понимаю ситуацию, когда кто-то должен избегать этого по разным причинам (например, работодатель требует сохранить старый способ). В этом случае я думаю, что мой ответ очень исчерпывающий и имеет большую ценность, чем ответы «только не делайте этого», потому что он указывает путь до конца. Покажите мне здесь другие ответы, которые выглядят так же, и я подумаю об удалении своего.
Хорошо, когда (не если) ваша система будет скомпрометирована, пожалуйста, вернитесь и удалите этот ответ ... или вы можете использовать параметризованный запрос.
@Hogan - У меня нет проблем с этим :) Но в настоящее время я утверждаю, что нет известного способа обойти это, если вы соблюдаете 4 правила, которые я опубликовал. Если вы действительно думаете, что есть способ обойти это, просто укажите где.
Плохой совет hombre. Интерполяция Любые может быть отклонена.
@Shayne - есть ли источник, подтверждающий это утверждение? Так что я и другие читатели могу извлечь из этого урок. Ваш комментарий не сильно отличается от приведенных выше, которые утверждают, что без какого-либо источника или указателя.
@BryanH Признание в непонимании того, как общепринятая мудрость применима к конкретному случаю, и просьба привести пример такого конкретного случая - это не высокомерие, а скромность. С другой стороны, раздражение, когда кто-то спрашивает, почему общепринятая мудрость верна, может показаться высокомерным. Рассуждение на конкретных примерах часто является отличным способом исследовать и учиться. То, как ОП решил это сомнение, было очень полезно для моего понимания предмета, особенно когда он объяснил найденный ответ.