Что такое sql инъекция

Инъекция SQL: руководство для начинающих

SQL

Внедрение кода или инъекция SQL — это уязвимость системы безопасности в Интернете, позволяющая злоумышленнику завладеть доступом к SQL-запросам в базу данных. Так он может получить конфиденциальную информацию о структуре базы данных, таблицах, столбцах или полях вместе со всеми данными, которые там есть.

Вот пример. Предположим, приложение использует следующий запрос на получение чьих-либо учётных данных:

Здесь имя пользователя username и пароль password — это данные, вводимые пользователем. Допустим, злоумышленник в оба поля введёт ‘ OR ‘1’=’1 . SQL-запрос будет выглядеть так:

На запрос приходит true, и доступ получен. Это пример самой простой SQL-инъекции.

Инъекция SQL может быть использована где угодно, и с её помощью можно получить любую, даже самую секретную информацию, хранящуюся в базе данных.

Примечание: пример максимально упрощён и приводится здесь исключительно в ознакомительных целях. В реальной жизни таких случаев практически не найти.

Можете заглянуть в эту шпаргалку и узнать, как выполнять запросы в базы данных SQL разных поставщиков.

Как нащупать уязвимость?

В большинстве случаев уязвимость обнаруживается введением неверных параметров, например: ‘ , » a’ or 1=1— , "a"" or 1=1—" , or a = a , a’ waitfor delay ‘0:0:10’— , 1 waitfor delay ‘0:0:10’— , %26 , ‘ or username like ‘% и т.д. После чего проявляются изменения в поведении приложения.

Можно проанализировать длину ответа сервера и время, требующееся для отправки этого ответа. Такая полезная нагрузка, как ‘ , a’ or 1=1— и т.д., может свидетельствовать об изменениях в ответе сервера баз данных. Но в случае отсутствия изменений мы можем попробовать запустить задержки по времени, используя полезную нагрузку a’ waitfor delay ‘0:0:10’— . Так можно задержать отправку ответа сервера на определённое время.

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

Но прежде надо определить количество столбцов , возвращаемых на SQL-запрос. Это важно, так как при несовпадении количества столбцов, которые мы пытаемся вытащить, с тем, что мы получим в ответе, нам вернётся ошибка.

Количество столбцов можно определить командой order by . Например:

Комментарии в SQL начинаются вот с такой комбинации символов — . Чтобы сохранить пробел после — , просто добавляем любой символ: так пробел не будет игнорироваться в HTTP-запросе . Для комментариев могут использоваться также # или /* */ в зависимости от поставщика базы данных SQL.

Продолжаем этот процесс, пока не появится ошибка. Если ошибка случилась во время использования полезной нагрузки order by 5 , а не order by 4 , значит, запрос возвращает 4 столбца.

Как использовать уязвимости

Обнаружив уязвимость приложения и определив количество столбцов, попробуем найти необходимую информацию о базе данных (БД): имя БД , имя пользователя БД , версия БД , имена таблиц , имена столбцов той или иной таблицы и т.д. Загляните в шпаргалку по инъекциям в SQL: там есть соответствующие запросы.

Что такое SQL-инъекции и как им противостоять

SQL-инъекции (SQL injections, SQLi) — самый хорошо изученный и простой для понимания тип атаки на веб-сайт или веб-приложение. Тем не менее, он странным образом остается весьма распространенным и в наши дни. Организация OWASP (Open Web Application Security Project) упоминает SQL-инъекции в своем документе OWASP Top 10 2017 как угрозу номер один для безопасности веб-приложений, и вряд ли положение сильно изменилось за четыре года.

Попасться на SQL-инъекцию — это все равно что в преферансе вистовать «стоя» на девятерной и в первый ход зайти с «голой» семерки или нарваться на «детский мат» в шахматах. Первое случилось с автором этих строк на первом курсе КПИ и стоило ему стипендии за месяц, а второе как минимум дважды происходило на официальных турнирах, согласно онлайн-базе данных. Итак, что же делает этот классический хакерский трюк таким живучим? Давайте разбираться.

SQL-инъекция database

Источник уязвимости — язык SQL

Почему вообще возможны SQL-инъекции?

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

Допустим, у нас есть веб-сайт онлайн-магазина, и пользователь вводит название продукта в строке поиска. Для обращения к данным в реляционных БД используется SQL (structured query language) — специальный язык, похожий на естественный (английский) язык. Этот язык стандартизирован (самый последний стандарт — SQL:2008), и основные его команды одинаковы для разных производителей СУБД — Microsoft, Oracle, MySQL, PostgreSQL и других.

Этот запрос (команды в SQL называют запросами, англ. Queries) создает в базе данных таблицу Users (пользователи) с тремя полями — целочисленным идентификатором ( Id ), именем ( Name ) и датой создания записи о пользователе ( CreationDate ).

Этот запрос удаляет таблицу с пользователями из базы данных (не данные, т.е. строки из таблицы, а саму таблицу; для удаления строк используется запрос DELETE — смотрите ниже):

Читает всех пользователей, созданных после 1 января 2021 года (в СУБД MS SQL Server).
DELETE FROM Users WHERE Id > 10
Удаляет всех пользователей с идентификатором большим 10
UPDATE Users SET CreationDate = ‘2020-12-01’
Устанавливает для всех (поскольку нет условия WHERE ) пользователей дату создания в 1 декабря 2020 года.

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

Например, если пользователь ищет на нашем сайте веб-магазина ноутбуки и вводит слово «ноутбук» в строке поиска, то соответствующий запрос может иметь (упрощенный) вид:

Оператор LIKE и символ подстановки “ % ” (wildcard character) используются для задания условия по подстроке.

Соответственно, фрагмент программы, который формирует этот запрос для выполнения, в простейшем случае формируется из шаблона команды SELECT с подставленным значением пользовательского ввода (C#):

Атака

Теперь предположим, что пользователь — злоумышленник, и вводит в строке поиска не название продукта, а вредоносный фрагмент SQL-кода, например:

a’; DROP TABLE Products; —

Тогда результирующий запрос будет иметь вид двух последовательных команд и одного комментария:

Комментарии в SQL имеют вид:

  1. /* это многострочный
  2. */ комментарий
  3. — однострочный комментарий

Итак, что мы видим? Введенный пользователем текст в форме поиска превратил SQL-команду выбора продуктов из таблицы в два запроса: первый — бессмысленный, а второй — вредоносный, удаляющий таблицу продуктов из базы и делающий наше приложение (веб-сайт) нежизнеспособным.

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

Реальные примеры

Наиболее часто применяются два вида атак SQL injection: Boolean-атака (Boolean Based SQLi) и UNION-атака (UNION Based SQLi).

Boolean-атака

В адресной строке браузера вводится запрос вида

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

UNION-атака

Ключевое слово UNION используется для объединения результатов двух и более запросов в один результат.

Например, у нас есть таблица продуктов:

Id Name Price
1 Ноутбук 29000
2 Карандаш 2

MS SQL Server

Мы получим такой результат (для БД MS SQL Server):Используя оператор UNION , злоумышленник может попытаться атаковать страницу поиска продуктов:

https://example.com/showProduct?id=1′ union select NULL,CURRENT_USER,NULL —

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

SQL database

Защита от SQL-инъекций

Параметризуйте это

Самое главное правило — данные, присылаемые пользователем, не должны участвовать в формировании текста SQL-запроса, во всяком случае напрямую. Достигается это использованием параметризированных (подготовленных) запросов.

Так, вместо подстановки (конкатенации) пользовательского ввода, надо использовать параметры, например, в случае C# вместо фрагмента, рассмотренного ранее:

Надо использовать следующий код:

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

Используйте хранимые процедуры

С технической точки зрения, это правило идентично предыдущему: пользовательский ввод не используется при динамической генерации SQL-запроса. Код хранимой процедуры неизменный и хранится в самой СУБД, а не в коде приложения.

Используйте белый список валидации

В некоторых случаях невозможно использовать параметризацию запросов.

Например, имя таблицы, из которой происходит выборка ( SELECT ), не может быть параметром, и в этом случае сам текст запроса формируется в зависимости от ввода пользователя.

В таком случае необходимо ограничить список допустимых значений, которые могут прийти от пользователя («белый список»).

Например, если из выпадающего списка веб-формы приходит значение Customers , то мы производим выборку из таблицы Customers , а если значение Supplier » — то из таблицы Suppliers .

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

Валидируйте пользовательский ввод

Вообще в работе с пользовательским вводом руководствуйтесь принципом «пользователь — всегда потенциальный злоумышленник». Бэкенд не имеет права слепо доверять ничему, что приходит с клиента, даже если клиентское приложение валидирует пользовательский ввод. Злоумышленник может использовать Swagger, автоматические скрипты и другие средства преодоления клиентской валидации.

Поменьше привилегий

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

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

Проверяйтесь

Всегда полезно проверять свое приложение на устойчивость, в том числе по отношению к SQLi-атакам. Одна из мощнейших и старейших утилит, предназначенных для поиска и устранения SQLi-уязвимостей — https://sqlmap.org/.

Заключение

В статье мы рассмотрели самый простой и самый распространенный тип атаки на веб-сайт — SQL-инъекцию. Давайте отметим основные тезисы:

  • Суть атаки заключается в попытке злоумышленника внедрить вредоносный SQL-код через легальный канал ввода (веб-форма, адресная строка браузера).
  • Наиболее эффективный способ защиты от SQL-инъекции — не использовать пользовательский ввод при построении SQL-запроса, а только в качестве значения параметров.
  • Всегда полезно валидировать (проверять) пользовательский ввод на стороне бэкенда. Пользователь — всегда злоумышленник.
  • Sqlmap — проверенное средство выявления SQLi-уязвимостей, и регулярное «обследование» им своего веб-приложения можно только приветствовать.

Ссылки

Качественное видео с дополнительной информаций по теме:

Примеры уязвимостей и противодействия им на разных языках программирования, основанные на чудесном комиксе xkcd: bobby-tables.com.

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

внедрение кода SQL;

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

Принцип действия атаки путем внедрения кода SQL

Основная форма атаки SQL Injection состоит в прямой вставке кода в пользовательские входные переменные, которые объединяются с командами SQL и выполняются. Менее явная атака внедряет небезопасный код в строки, предназначенные для хранения в таблице или в виде метаданных. Когда впоследствии сохраненные строки объединяются с динамической командой SQL, происходит выполнение небезопасного кода.

Атака осуществляется посредством преждевременного завершения текстовой строки и присоединения к ней новой команды. Поскольку к вставленной команде перед выполнением могут быть добавлены дополнительные строки, злоумышленник заканчивает внедряемую строку меткой комментария «—». Весь последующий текст во время выполнения не учитывается.

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

Пользователю выводится запрос на ввод названия города. Если пользователь вводит Redmond , то запрос, построенный с помощью скрипта, выглядит приблизительно так:

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

В этом случае запрос, построенный скриптом, будет следующим:

Точка с запятой «;» обозначает конец одного запроса и начало другого. А последовательность двух дефисов (—) означает, что остальная часть текущей строки является комментарием и не должна обрабатываться. Если измененный код будет синтаксически правилен, то он будет выполнен сервером. Когда SQL Server будет обрабатывать эту инструкцию, SQL Server прежде всего отберет все записи в OrdersTable , где ShipCity является Redmond . Затем SQL Server удалит OrdersTable .

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

Проверка достоверности всех вводимых данных

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

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

Как приложение будет вести себя, если пользователь по ошибке или по злому умыслу вставит MPEG-файл размером 10 МБ там, где приложение ожидает ввод почтового индекса?

Как приложение будет вести себя, если в текстовое поле будет внедрена инструкция DROP TABLE ?

Проверьте размер и тип вводимых данных и установите соответствующие ограничения. Это поможет предотвратить преднамеренное переполнение буфера.

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

При работе с XML-документами проверяйте все вводимые данные на соответствие схеме.

Никогда не создавайте инструкции Transact-SQL непосредственно из данных, вводимых пользователем.

Для проверки вводимых пользователем данных используйте хранимые процедуры.

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

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

Никогда не объединяйте введенные пользователем данные без проверки. Объединение строк является основной точкой входа для внедрения скрипта.

Не допускайте использование в полях следующих строк, из которых могут быть созданы имена файлов: AUX, CLOCK$, COM1–COM8, CON, CONFIG$, LPT1–LPT8, NUL и PRN.

По возможности отклоняйте вводимые данные, содержащие следующие символы:

Входной символ Значение в языке Transact-SQL
; Разделитель запросов.
Разделитель строк символьных данных.
Разделитель однострочного комментария. Текст после и до конца этой строки не обрабатывается сервером.
/*_ . _*/ Разделители комментариев. Сервер не обрабатывает текст между знаками /* _ и _ */ .
xp_ Используется в начале имени расширенных хранимых процедур каталога, например xp_cmdshell .

Использование SQL-параметров безопасных типов

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

В этом примере параметр @au_id обрабатывается как буквенное значение, а не исполняемый код. Это значение проверяется по типу и длине. Если значение @au_id не соответствует указанным ограничениям типа и длины, то будет вызвано исключение.

Использование параметризованного ввода с хранимыми процедурами

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

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

Использование коллекции Parameters с динамическим SQL

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

Фильтрация ввода

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

Предложения LIKE

Обратите внимание, что при использовании предложения LIKE подстановочные знаки по-прежнему нужно выделять escape-символами:

Просмотр кода на предмет возможности атаки SQL Injection

Необходимо просматривать все фрагменты кода, вызывающие инструкции EXECUTE , EXEC или sp_executesql . Чтобы выявить процедуры, содержащие эти инструкции, можно использовать запросы, подобные следующему. Этот запрос проверяет наличие 1, 2, 3 или 4 пробелов после слов EXECUTE и EXEC .

Упаковка параметров с помощью функций QUOTENAME() и REPLACE()

Убедитесь, что в каждой выбранной хранимой процедуре все используемые в динамическом Transact-SQL переменные обрабатываются правильно. Данные, поступающие через входные параметры хранимой процедуры или считываемые из таблицы, должны быть помещены в функции QUOTENAME() или REPLACE(). Помните, что значение @variable, передаваемое функции QUOTENAME(), принадлежит к типу sysname и имеет ограничение длины в 128 символов.

@переменная Рекомендуемый упаковщик
Имя защищаемого объекта QUOTENAME(@variable)
Строка, содержащая не более 128 знаков. QUOTENAME(@variable, »»)
Строка из > 128 символов REPLACE(@variable,»», »»»)

При использовании этого метода инструкция SET может быть исправлена следующим образом:

Атака Injection, проводимая с помощью усечения данных

Любое присваивАՐܐސпеременной диݐАܐؑǐՑPڐސзначение Transact-SQL усекается, если оно не вмещается в буфер, назначенный для этой переменной. Если организатор атаки способен обеспечить усечение инструкции, передавая хранимой процедуре непредвиденно длинные строки, он получает возможность манипулировать результатом. Так, хранимая процедура, создаваемая с помощью следующего скрипта, уязвима для атаки Injection, проводимой методом усечения.

Передавая 154 знака в буфер, рассчитанный на 128 знаков, злоумышленник может установить новый пароль для sa, не зная старого пароля.

Поэтому для переменной команды следует использовать большой буфер или непосредственно выполнять динамический Transact-SQL внутри инструкции EXECUTE .

Усечение при использовании функций QUOTENAME(@variable, »») и REPLACE()

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

Таким образом, следующая инструкция установит для паролей всех пользователей значение, переданное предыдущим фрагментом кода.

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

Как и в случае с функцией QUOTENAME(), усечения строки с помощью функции REPLACE() можно избежать, объявив временные переменные, достаточно большие для всех случаев. По возможности функции QUOTENAME() или REPLACE() следует вызывать непосредственно внутри динамического Transact-SQL. Или же необходимый размер буфера можно рассчитать следующим образом. Для @outbuffer = QUOTENAME(@input) размер буфера переменной @outbuffer должен составлять 2*(len(@input)+1) . При использовании функции REPLACE() и двойных кавычек, как в предыдущем примере, достаточно буфера размером 2*len(@input) .

Следующий расчет применим ко всем случаям.

Усечение при использовании функции QUOTENAME(@variable, ‘]’)

Усечение может произойти, когда имя защищаемого объекта SQL Server передается инструкциям, которые используют форму функции QUOTENAME(@variable, ‘]’) . В следующем примере приведена иллюстрация этого.

При сцеплении значений типа sysname рекомендуется использовать временные переменные достаточно больших размеров, чтобы они могли вмещать до 128 знаков на одно значение. По возможности функцию QUOTENAME() следует вызывать непосредственно внутри динамического Transact-SQL. Или же необходимый размер буфера можно рассчитать, как это показано в предыдущем разделе.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *