Можно ли защитить от внедрения SQL путем экранирования одинарных кавычек и ввода данных пользователем в одинарные кавычки?
===========================================================================================================================

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

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

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

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

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

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

 

---

 ОБНОВИТЬ:

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

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

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

 

Прежде всего, это просто плохая практика. Валидация входных данных всегда необходима, но она также всегда iffy.   

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

* <http://www.imperva.com/docs/WP_SQL_Injection_Protection_LK.pdf>
* <http://www.it-docs.net/ddata/4954.pdf> (раскрытие, это последнее было моим;))
* <https://www.owasp.org/images/d/d4/OWASP_IL_2007_SQL_Smuggling.pdf> (на основе предыдущей статьи, которая больше не доступна)

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

 (function(w, d, n, s, t) {
 w[n] = w[n] || [];
 w[n].push(function() {
 Ya.Context.AdvManager.render({
 blockId: "R-A-248011-3",
 renderTo: "yandex\_rtb\_R-A-248011-3",
 async: true
 });
 });
 t = d.getElementsByTagName("script")[0];
 s = d.createElement("script");
 s.src = "//an.yandex.ru/system/context.js";
 s.async = true;
 t.parentNode.insertBefore(s, t);
 })(this, this.document, "yandexContextAsyncCallbacks");

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

Правильный способ сделать это:

* Валидация белого списка: тип, длина, формат или принятые значения
* Если вы хотите попасть в черный список, идите прямо вперед. Цитата escaping хороша, но в контексте других смягчений.
* Использовать объекты Command и Parameter для подготовки и проверки
* Запросить только параметризованные запросы.
* Еще лучше, используйте только хранимые процедуры.
* Избегайте использования динамического SQL и не используйте конкатенацию строк для создания запросов.
* Если вы используете SP, вы также можете ограничить разрешения в базе данных только для выполнения необходимых SP, а не напрямую обращаться к таблицам.
* вы также можете легко убедиться, что вся база кода только обращается к БД через SP...

 

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

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

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

>  sSanitizedInput = "'" и заменить (sInput, "'" , "''" ) и "'" 
> 
> 
> 

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

 (function(w, d, n, s, t) {
 w[n] = w[n] || [];
 w[n].push(function() {
 Ya.Context.AdvManager.render({
 blockId: "R-A-248011-4",
 renderTo: "yandex\_rtb\_R-A-248011-4",
 async: true
 });
 });
 t = d.getElementsByTagName("script")[0];
 s = d.createElement("script");
 s.src = "//an.yandex.ru/system/context.js";
 s.async = true;
 t.parentNode.insertBefore(s, t);
 })(this, this.document, "yandexContextAsyncCallbacks");

* SQL Injection второго порядка - если SQL-запрос перестраивается на основе данных, полученных из базы данных  **после экранирования**, данные объединяются без привязки и могут быть косвенно введены SQL. См.
* Усечение строк - (немного сложнее) - Сценарий: у вас есть два поля, например имя пользователя и пароль, а SQL объединяет их оба. И оба поля (или только первое) имеют жесткий предел по длине. Например, имя пользователя ограничено 20 символами. Скажем, у вас есть этот код:

> 
> 
> ```
> username = left(Replace(sInput, "'", "''"), 20)
> 
> ```
> 
> 

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

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

```

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

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

 

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

 

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

Один из способов запуска атаки на процедуру "quote the argument" - с усечением строки.
Согласно MSDN, в SQL Server 2000 SP4 (и SQL Server 2005 с пакетом обновления 1) слишком длинная строка будет тихо усечена.

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

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

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

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

 

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

 

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

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

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

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

 

Это плохая идея, поскольку вы, кажется, знаете.

Что-то вроде ускорения цитаты в строке:\

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

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

 

Простой ответ: он будет работать иногда, но не все время.
Вы хотите использовать проверку белого списка на  **все**, но я понимаю, что это не всегда возможно, поэтому вы вынуждены идти с лучшим списком догадок. Аналогично, вы хотите использовать параметризованные хранимые procs в  **все**, но еще раз это не всегда возможно, поэтому вы вынуждены использовать sp\_execute с параметрами.

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

Приятная запись здесь: [http://www.owasp.org/index.php/Top\_10\_2007-A2](https://www.owasp.org/index.php/Top_10_2007-A2)

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

 

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

 

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

 

Да, это должно работать до тех пор, пока кто-то не выполнит [SET QUOTED\_IDENTIFIER OFF](https://msdn.microsoft.com/en-us/library/ms174393.aspx) и использует двойную кавычку.

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

>  Драйвер 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, если вы этого не знаете. По общему признанию - это не тот курячий пистолет, который вы ищете, но это довольно большая поверхность атаки. Конечно, если вы также избежали двойных кавычек - тогда мы вернемся туда, где мы начали.;)

 

Ваша защита потерпит неудачу, если:

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

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

 

Патрик, добавляете ли вы одиночные кавычки вокруг ВСЕГО ввода, даже числового ввода? Если у вас есть числовой ввод, но они не помещают одинарные кавычки вокруг него, тогда у вас есть экспозиция.

 

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

Также зачем изобретать колесо?

 

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

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

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

 

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

Это много дополнительной работы.

 

 @BryanH Мне нравится видеть этот ответ: "Посмотрите на ответы ниже и не думайте, что вы можете изобретать велосипед и делать лучше - лучше! --than всех, кто ушел раньше".

  Теперь я прошу вас обратить ваше внимание на беспроводное соединение, которым вы пользуетесь, и на ваш мобильный телефон, и на многие другие. Я не буду утомлять всех подробностями. Они родились в сознании Теслы и улучшились почти через 100 лет. Ты водишь Ford 1912 года? Почему мы хотим, чтобы что-то было улучшено? Читая избиение себя праведными комментариями, вот почему я не размещал и почти не использую этот сайт. Не троллите, если вы не можете дать конструктивную критику и помощь, а затем следуйте старой поговорке: если вы не можете сказать что-то хорошее ,,, если потеряно, попробуйте Google, что-то еще, что было переизобретением старого продукта. Хотелось бы, чтобы рядом с вашим комментарием было пониженное голосование. Приношу свои извинения за разглагольствование всем тем, кто это рассматривает.

 

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

 

 Да, вы можете, если...
=======================

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

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

	+ Операторы SQL являются проблематичными для проверки
* вы используете либо столбцы `nvarchar`/`nchar` (и префиксные строковые литералы с помощью `N`), либо предельные значения, входящие в столбцы `varchar`/`char` только для символов ASCII (например, исключение throw при создании SQL)

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

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

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

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