Что такое sql инъекция
SQL-инъекция — это техника, при которой злоумышленник использует недостатки в коде приложения, отвечающего за построение динамических SQL-запросов. Злоумышленник может получить доступ к привилегированным разделам приложения, получить всю информацию из базы данных, подменить существующие данные или даже выполнить опасные команды системного уровня на узле базы данных. Уязвимость возникает, когда разработчики конкатенируют или интерполируют произвольный ввод в SQL-запросах.
Пример #1 Постраничный вывод результата и создание суперпользователя в PostgreSQL
В следующем примере пользовательский ввод напрямую интерполируется в SQL-запрос, что позволяет злоумышленнику получить учётную запись суперпользователя в базе данных.
$offset = $_GET [ ‘offset’ ]; // осторожно, нет валидации ввода!
$query = «SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset ;» ;
$result = pg_query ( $conn , $query );
?>
Обычно пользователи нажимают по ссылкам ‘вперёд’ и ‘назад’, вследствие чего значение переменной $offset заносится в URL . Скрипт ожидает, что $offset — десятичное число. Однако, взломщик может попытаться взломать систему, присоединив к URL следующее значение:
0; insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd) select 'crack', usesysid, 't','t','crack' from pg_shadow where usename='postgres'; --
Если это произойдёт, скрипт предоставит злоумышленнику доступ суперпользователя. Обратите внимание, что значение 0; использовано для того, чтобы задать правильное смещение для первого запроса и корректно его завершить.
Замечание:
Это распространённый приём, чтобы заставить синтаксический анализатор SQL игнорировать остальную часть запроса, написанного разработчиком с помощью — , который является знаком комментария в SQL.
Ещё один вероятный способ получить пароли учётных записей в БД — атака страниц, предоставляющих поиск по базе. Злоумышленнику нужно лишь проверить, используется ли в запросе передаваемая на сервер и необрабатываемая надлежащим образом переменная. Это может быть один из устанавливаемых на предыдущей странице фильтров, таких как WHERE, ORDER BY, LIMIT и OFFSET , используемых при построении запросов SELECT . В случае, если используемая вами база данных поддерживает конструкцию UNION , злоумышленник может присоединить к оригинальному запросу ещё один дополнительный, для извлечения пользовательских паролей. Настоятельно рекомендуем использовать только зашифрованные пароли.
Пример #2 Листинг статей. и некоторых паролей (для любой базы данных)
$query = «SELECT id, name, inserted, size FROM products
WHERE size = ‘ $size ‘» ;
$result = odbc_exec ( $conn , $query );
?>
Статическая часть запроса может комбинироваться с другим SELECT -запросом, который выведет все пароли:
' union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable; --
Выражения UPDATE и INSERT также подвержены таким атакам.
Пример #3 От сброса пароля до получения дополнительных привилегий (любой сервер баз данных)
$query = «UPDATE usertable SET pwd=’ $pwd ‘ WHERE uid=’ $uid ‘;» ;
?> ?php
Но злоумышленник может ввести значение ‘ or uid like’%admin%’ для переменной $uid для изменения пароля администратора или просто присвоить переменной $pwd значение hehehe’, trusted=100, admin=’yes для получения дополнительных привилегий. При выполнении запросы переплетаются:
// $uid: ‘ or uid like ‘%admin%
$query = «UPDATE usertable SET pwd=’. ‘ WHERE uid=» or uid like ‘%admin%’;» ;
// $pwd: hehehe’, trusted=100, admin=’yes
$query = «UPDATE usertable SET pwd=’hehehe’, trusted=100, admin=’yes’ WHERE
. ;» ;
?>
Хотя остаётся очевидным, что для проведения успешной атаки злоумышленник должен обладать хотя бы некоторыми знаниями об архитектуре базы данных, получить эту информацию зачастую очень просто. Например, код может быть частью программного обеспечения с открытым исходным кодом и находиться в открытом доступе. Эта информация также может быть раскрыта закрытым кодом — даже если он закодирован, обфусцирован или скомпилирован, и даже вашим собственным кодом через отображение сообщений об ошибках. Другие методы включают использование типичных имён таблиц и столбцов. Например, форма входа в систему, использующая таблицу ‘users’ с именами столбцов ‘id’, ‘username’ и ‘password’.
Пример #4 Атака на операционную систему сервера базы данных (MSSQL Server)
Пугающий пример того, как команды уровня операционной системы могут быть доступны на некоторых узлах баз данных.
$query = «SELECT * FROM products WHERE id LIKE ‘% $prod %'» ;
$result = mssql_query ( $query );
?>
Если злоумышленник передаст значение a%’ exec master..xp_cmdshell ‘net user test testpass /ADD’ — в $prod , то $query будет:
$query = «SELECT * FROM products
WHERE id LIKE ‘%a%’
exec master..xp_cmdshell ‘net user test testpass /ADD’ —%'» ;
$result = mssql_query ( $query );
?>
MSSQL Server выполняет SQL запросы в пакете, включая команду добавления нового пользователя в локальную базу данных учётных записей. Если бы это приложение было запущено от имени sa и служба MSSQLSERVER была запущена с достаточными привилегиями, у злоумышленника появилась бы учётная запись, с помощью которой он мог бы получить доступ к этой машине.
Замечание:
Некоторые примеры, приведённые выше, привязаны к конкретному серверу баз данных, но это не означает, что подобная атака невозможна на другие продукты. Ваш сервер баз данных может быть аналогично уязвим и другим способом.
Изображение любезно предоставлено » xkcd
Способы защиты
Рекомендуемый способ избежать SQL-инъекций — связывание всех данных с помощью подготовленных запросов. Использование подготовленных запросов недостаточно для полного предотвращения SQL-инъекций, но это самый простой и безопасный способ обеспечить ввод данных в SQL-запросы. Все динамические литералы данных в выражениях WHERE , SET и VALUES должны быть заменены заполнителями. Фактические данные будут связаны во время выполнения и отправлены отдельно от команды SQL.
Привязка параметров может использоваться только для данных. Другие динамические части SQL-запроса должны быть отфильтрованы по известному списку допустимых значений.
Пример #5 Избегание SQL-инъекций с помощью подготовленных операторов PDO
// Динамическая часть SQL проверяется на соответствие ожидаемым значениям
$sortingOrder = $_GET [ ‘sortingOrder’ ] === ‘DESC’ ? ‘DESC’ : ‘ASC’ ;
$productId = $_GET [ ‘productId’ ];
// SQL подготавливается с заполнителем
$stmt = $pdo -> prepare ( «SELECT * FROM products WHERE id LIKE ? ORDER BY price < $sortingOrder >» );
// Значение предоставляется с подстановочными знаками LIKE
$stmt -> execute ([ «% < $productId >%» ]);
?>
Подготовленные операторы предоставляются PDO, MySQLi, а также другими библиотеками баз данных.
Атаки SQL-инъекций в основном основаны на использовании кода, написанного без учёта требований безопасности. Никогда не доверяйте любому вводу, особенно со стороны клиента, даже если он поступает из поля выбора, скрытого поля ввода или cookie. Первый пример показывает, что такой простой запрос может привести к катастрофе.
- Никогда не подключайтесь к базе данных как суперпользователь или владелец базы данных. Всегда используйте настроенных пользователей с минимальными привилегиями.
- Всегда проверяйте введённые данные на соответствие ожидаемому типу. В PHP есть множество функций для проверки данных: начиная от простейших функций для работы с переменными и функций определения типа символов (таких как is_numeric() и ctype_digit() соответственно) и заканчивая Perl-совместимыми регулярными выражениями.
- В случае, если приложение ожидает цифровой ввод, примените функцию ctype_digit() для проверки введённых данных, или принудительно укажите их тип при помощи settype() , или просто используйте числовое представление при помощи функции sprintf() .
- Если на уровне базы данных не поддерживаются привязанные переменные, то всегда экранируйте любые нечисловые данные, используемый в запросах к БД при помощи специальных экранирующих функций, специфичных для используемой вами базы данных (например, mysql_real_escape_string() , sqlite_escape_string() и т.д.). Общие функции такие как addslashes() полезны только в определённых случаях (например MySQL в однобайтной кодировке с отключённым NO_BACKSLASH_ESCAPES ), поэтому лучше избегать их использование.
- Ни в коем случае не выводите никакой информации о БД, особенно о её структуре. Также ознакомьтесь с соответствующими разделами документации: «Сообщения об ошибках» и «Функции обработки и логирования ошибок».
Помимо всего вышесказанного, вы можете логировать запросы в вашем скрипте либо на уровне базы данных, если она это поддерживает. Очевидно, что логирование не может предотвратить нанесение ущерба, но может помочь при трассировке взломанного приложения. Лог-файл полезен не сам по себе, а информацией, которая в нем содержится. Причём, в большинстве случаев полезно логировать все возможные детали.
Атака путем внедрения кода SQL
Внедрение кода SQL — это атака, во время которой вредоносный код вставляется в строки, которые позже будут переданы на экземпляр SQL Server для анализа и выполнения. Любая процедура, создающая инструкции SQL, должна рассматриваться на предмет уязвимости к внедрению кода, так как SQL Server выполняет все получаемые синтаксически правильные запросы. Даже параметризованные данные могут стать предметом манипуляций опытного злоумышленника.
Принцип действия атаки путем внедрения кода SQL
Основная форма атаки SQL Injection состоит в прямой вставке кода в пользовательские входные переменные, которые объединяются с командами SQL и выполняются. Менее явная атака внедряет небезопасный код в строки, предназначенные для хранения в таблице или в виде метаданных. Когда впоследствии сохраненные строки объединяются с динамической командой SQL, происходит выполнение небезопасного кода.
Атака осуществляется посредством преждевременного завершения текстовой строки и присоединения к ней новой команды. Поскольку к вставленной команде перед выполнением могут быть добавлены дополнительные строки, злоумышленник заканчивает внедряемую строку меткой комментария «—». Весь последующий текст во время выполнения не учитывается.
Следующий скрипт показывает простую атаку SQL Injection. Скрипт формирует SQL-запрос, выполняя объединение жестко запрограммированных строк со строкой, введенной пользователем:
var ShipCity; ShipCity = Request.form ("ShipCity"); var sql = "select * from OrdersTable where ShipCity = '" + ShipCity + "'";
Пользователю выводится запрос на ввод названия города. Если они вводят Redmond , запрос, собранный скриптом, выглядит следующим образом:
SELECT * FROM OrdersTable WHERE ShipCity = 'Redmond'
Предположим, однако, что пользователь вводит следующее:
Redmond'; drop table OrdersTable--
В этом случае запрос, построенный скриптом, будет следующим:
SELECT * FROM OrdersTable WHERE ShipCity = 'Redmond';drop table OrdersTable--'
Точка с запятой «;» обозначает конец одного запроса и начало другого. А последовательность двух дефисов (—) означает, что остальная часть текущей строки является комментарием и не должна обрабатываться. Если измененный код будет синтаксически правилен, то он будет выполнен сервером. Когда SQL Server обрабатывает эту инструкцию, SQL Server сначала выбирает все записи, в которых OrdersTable ShipCity находится Redmond . Затем SQL Server упадет OrdersTable .
Если вставленный код SQL синтаксически верен, искаженные данные нельзя выявить программно. Поэтому необходимо проверять правильность всех вводимых пользователями данных, а также внимательно просматривать код, выполняющий созданные с помощью SQL команды на сервере. Рекомендуемые приемы программирования описываются в следующих подразделах этого раздела.
Проверка достоверности всех вводимых данных
Всегда проверяйте все данные, вводимые пользователем, выполняя проверку типа, длины, формата и диапазона данных. При реализации мер предосторожности, направленных против злонамеренного ввода данных, учитывайте архитектуру и сценарии развертывания приложения. Помните, что программы, созданные для работы в безопасной среде, могут быть скопированы в небезопасную среду. Рекомендуется следующая стратегия:
- Не делайте никаких предположений о размере, типе или содержимом данных, получаемых приложением. Например, рекомендуется оценить следующее.
- Как приложение будет вести себя, если пользователь по ошибке или по злому умыслу вставит MPEG-файл размером 10 МБ там, где приложение ожидает ввод почтового индекса?
- Как приложение будет вести себя, если в текстовое поле будет внедрена инструкция DROP TABLE ?
По возможности отклоняйте вводимые данные, содержащие следующие символы:
Входной символ Значение в языке Transact-SQL ; Разделитель запросов. ‘ Разделитель строк символьных данных. — Разделитель однострочного комментария. Текст после — и до конца этой строки не обрабатывается сервером. /* . */ Разделители комментариев. Сервер не обрабатывает текст между знаками /* и */ . xp_ Используется в начале имени расширенных хранимых процедур каталога, например xp_cmdshell . Использование SQL-параметров безопасных типов
Коллекция параметров в SQL Server предоставляет проверку типа проверка и длины. Если используется коллекция Parameters , то вводимые данные обрабатываются как буквенное значение, а не исполняемый код. Дополнительное преимущество использования коллекции Parameters состоит в том, что можно использовать принудительные проверки типа и длины данных. Если значение выходит за рамки диапазона, будет вызвано исключение. В следующем фрагменте кода демонстрируется использование коллекции Parameters :
SqlDataAdapter myCommand = new SqlDataAdapter("AuthorLogin", conn); myCommand.SelectCommand.CommandType = CommandType.StoredProcedure; SqlParameter parm = myCommand.SelectCommand.Parameters.Add("@au_id", SqlDbType.VarChar, 11); parm.Value = Login.Text;
В этом примере параметр @au_id обрабатывается как буквенное значение, а не исполняемый код. Это значение проверяется по типу и длине. Если значение @au_id не соответствует указанным ограничениям типа и длины, то будет вызвано исключение.
Использование параметризованного ввода с хранимыми процедурами
Хранимые процедуры могут быть подвержены атакам SQL Injection, если они используют нефильтрованные входные данные. Например, следующий код является уязвимым:
SqlDataAdapter myCommand = new SqlDataAdapter("LoginStoredProcedure '" + Login.Text + "'", conn);
Если используются хранимые процедуры, то в качестве их входных данных следует использовать параметры.
Использование коллекции Parameters с динамическим SQL
Если невозможно использовать хранимые процедуры, сохраняется возможность использования параметров, как показано в следующем примере кода:
SqlDataAdapter myCommand = new SqlDataAdapter( "SELECT au_lname, au_fname FROM Authors WHERE au_id = @au_id", conn); SQLParameter parm = myCommand.SelectCommand.Parameters.Add("@au_id", SqlDbType.VarChar, 11); Parm.Value = Login.Text;
Фильтрация ввода
Для защиты от атак SQL injection посредством удаления escape-символов можно также использовать фильтрацию ввода. Однако этот метод защиты не является надежным в связи с тем, что проблемы может создавать большое число символов. В следующем примере производится поиск разделителей символьных строк:
private string SafeSqlLiteral(string inputSQL)
Предложения LIKE
Обратите внимание, что при использовании предложения LIKE подстановочные знаки по-прежнему нужно выделять escape-символами:
s = s.Replace("[", "[[]"); s = s.Replace("%", "[%]"); s = s.Replace("_", "[_]");
Просмотр кода на предмет возможности атаки SQL Injection
Необходимо просматривать все фрагменты кода, вызывающие инструкции EXECUTE , EXEC или sp_executesql . Чтобы выявить процедуры, содержащие эти инструкции, можно использовать запросы, подобные следующему. Этот запрос проверяет наличие 1, 2, 3 или 4 пробелов после слов EXECUTE и EXEC .
SELECT object_Name(id) FROM syscomments WHERE UPPER(TEXT) LIKE '%EXECUTE (%' OR UPPER(TEXT) LIKE '%EXECUTE (%' OR UPPER(TEXT) LIKE '%EXECUTE (%' OR UPPER(TEXT) LIKE '%EXECUTE (%' OR UPPER(TEXT) LIKE '%EXEC (%' OR UPPER(TEXT) LIKE '%EXEC (%' OR UPPER(TEXT) LIKE '%EXEC (%' OR UPPER(TEXT) LIKE '%EXEC (%' OR UPPER(TEXT) LIKE '%SP_EXECUTESQL%';
Упаковка параметров с помощью функций QUOTENAME() и REPLACE()
Убедитесь, что в каждой выбранной хранимой процедуре все используемые в динамическом Transact-SQL переменные обрабатываются правильно. Данные, поступающие через входные параметры хранимой процедуры или считываемые из таблицы, должны быть помещены в функции QUOTENAME() или REPLACE(). Помните, что значение @variable, передаваемое функции QUOTENAME(), принадлежит к типу sysname и имеет ограничение длины в 128 символов.
@variable Рекомендуемый упаковщик Имя защищаемого объекта QUOTENAME(@variable) Строка, содержащая не более 128 знаков. QUOTENAME(@variable, »») > Строка из 128 символов REPLACE(@variable,»», »»») При использовании этого метода инструкция SET может быть исправлена следующим образом:
-- Before: SET @temp = N'SELECT * FROM authors WHERE au_lname =''' + @au_lname + N''''; -- After: SET @temp = N'SELECT * FROM authors WHERE au_lname = ''' + REPLACE(@au_lname, '''', '''''') + N'''';
Атака Injection, проводимая с помощью усечения данных
Любая динамическая переменная Transact-SQL, назначенная переменной, будет усечена, если она больше буфера, выделенного для этой переменной. Если организатор атаки способен обеспечить усечение инструкции, передавая хранимой процедуре непредвиденно длинные строки, он получает возможность манипулировать результатом. Так, хранимая процедура, создаваемая с помощью следующего скрипта, уязвима для атаки Injection, проводимой методом усечения.
CREATE PROCEDURE sp_MySetPassword @loginname SYSNAME, @old SYSNAME, @new SYSNAME AS -- Declare variable. -- Note that the buffer here is only 200 characters long. DECLARE @command VARCHAR(200) -- Construct the dynamic Transact-SQL. -- In the following statement, we need a total of 154 characters -- to set the password of 'sa'. -- 26 for UPDATE statement, 16 for WHERE clause, 4 for 'sa', and 2 for -- quotation marks surrounded by QUOTENAME(@loginname): -- 200 - 26 - 16 - 4 - 2 = 154. -- But because @new is declared as a sysname, this variable can only hold -- 128 characters. -- We can overcome this by passing some single quotation marks in @new. SET @command = 'update Users set password=' + QUOTENAME(@new, '''') + ' where username=' + QUOTENAME(@loginname, '''') + ' AND password = ' + QUOTENAME(@old, '''') -- Execute the command. EXEC (@command) GO
Передавая 154 знака в буфер, рассчитанный на 128 знаков, злоумышленник может установить новый пароль для sa, не зная старого пароля.
EXEC sp_MySetPassword 'sa', 'dummy', '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012'''''''''''''''''''''''''''''''''''''''''''''''''''
По этой причине следует использовать большой буфер для переменной команды или напрямую выполнить динамический Transact-SQL внутри инструкции EXECUTE .
Усечение при использовании функций QUOTENAME(@variable, »») и REPLACE()
Если строки, возвращаемые функциями QUOTENAME() и REPLACE(), не умещаются в выделенном пространстве, они усекаются без взаимодействия с пользователем. Хранимая процедура, создаваемая в следующем примере, показывает, что может произойти.
CREATE PROCEDURE sp_MySetPassword @loginname SYSNAME, @old SYSNAME, @new SYSNAME AS -- Declare variables. DECLARE @login SYSNAME DECLARE @newpassword SYSNAME DECLARE @oldpassword SYSNAME DECLARE @command VARCHAR(2000) -- In the following statements, the data stored in temp variables -- will be truncated because the buffer size of @login, @oldpassword, -- and @newpassword is only 128 characters, but QUOTENAME() can return -- up to 258 characters. SET @login = QUOTENAME(@loginname, '''') SET @oldpassword = QUOTENAME(@old, '''') SET @newpassword = QUOTENAME(@new, '''') -- Construct the dynamic Transact-SQL. -- If @new contains 128 characters, then @newpassword will be '123. n -- where n is the 127th character. -- Because the string returned by QUOTENAME() will be truncated, -- it can be made to look like the following statement: -- UPDATE Users SET password ='1234. . .[127] WHERE username=' -- other stuff here SET @command = 'UPDATE Users set password = ' + @newpassword + ' where username = ' + @login + ' AND password = ' + @oldpassword; -- Execute the command. EXEC (@command); GO
Таким образом, следующая инструкция установит для паролей всех пользователей значение, переданное предыдущим фрагментом кода.
EXEC sp_MyProc '--', 'dummy', '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678'
Возможно выполнить принудительное усечение строки, для чего нужно превысить выделенное для буфера пространство при использовании функции REPLACE(). Хранимая процедура, создаваемая в следующем примере, показывает, что может произойти.
CREATE PROCEDURE sp_MySetPassword @loginname SYSNAME, @old SYSNAME, @new SYSNAME AS -- Declare variables. DECLARE @login SYSNAME DECLARE @newpassword SYSNAME DECLARE @oldpassword SYSNAME DECLARE @command VARCHAR(2000) -- In the following statements, data will be truncated because -- the buffers allocated for @login, @oldpassword and @newpassword -- can hold only 128 characters, but QUOTENAME() can return -- up to 258 characters. SET @login = REPLACE(@loginname, '''', '''''') SET @oldpassword = REPLACE(@old, '''', '''''') SET @newpassword = REPLACE(@new, '''', '''''') -- Construct the dynamic Transact-SQL. -- If @new contains 128 characters, @newpassword will be '123. n -- where n is the 127th character. -- Because the string returned by QUOTENAME() will be truncated, it -- can be made to look like the following statement: -- UPDATE Users SET password='1234. [127] WHERE username=' -- other stuff here SET @command = 'update Users set password = ''' + @newpassword + ''' where username = ''' + @login + ''' AND password = ''' + @oldpassword + ''''; -- Execute the command. EXEC (@command); GO
Как и в случае с функцией QUOTENAME(), усечения строки с помощью функции REPLACE() можно избежать, объявив временные переменные, достаточно большие для всех случаев. По возможности необходимо вызвать QUOTENAME() или REPLACE() непосредственно внутри динамического Transact-SQL. Или же необходимый размер буфера можно рассчитать следующим образом. Для @outbuffer = QUOTENAME(@input) размер буфера переменной @outbuffer должен составлять 2*(len(@input)+1) . При использовании функции REPLACE() и двойных кавычек, как в предыдущем примере, достаточно буфера размером 2*len(@input) .
Следующий расчет применим ко всем случаям.
WHILE LEN(@find_string) > 0, required buffer size = ROUND(LEN(@input) / LEN(@find_string), 0) * LEN(@new_string) + (LEN(@input) % LEN(@find_string))
Усечение при использовании функции QUOTENAME(@variable, ‘]’)
Усечение может возникать, когда имя защищаемого объекта SQL Server передается в инструкции, использующие форму QUOTENAME(@variable, ‘]’) . В следующем примере приведена иллюстрация этого.
CREATE PROCEDURE sp_MyProc @schemaname sysname, @tablename sysname, AS -- Declare a variable as sysname. The variable will be 128 characters. -- But @objectname actually must allow for 2*258+1 characters. DECLARE @objectname sysname SET @objectname = QUOTENAME(@schemaname)+'.'+ QUOTENAME(@tablename) -- Do some operations. GO
При сцеплении значений типа sysname рекомендуется использовать временные переменные достаточно больших размеров, чтобы они могли вмещать до 128 знаков на одно значение. По возможности вызовите QUOTENAME() непосредственно внутри динамического Transact-SQL. Или же необходимый размер буфера можно рассчитать, как это показано в предыдущем разделе.
SQL инъекции. Проверка, взлом, защита
SQL инъекция — это один из самых доступных способов взлома сайта.
Суть таких инъекций – внедрение в данные (передаваемые через GET, POST запросы или значения Cookie) произвольного SQL кода. Если сайт уязвим и выполняет такие инъекции, то по сути есть возможность творить с БД (чаще всего это MySQL) что угодно.Как вычислить уязвимость, позволяющую внедрять SQL инъекции?
Довольно легко. Например, есть тестовый сайт test.ru. На сайте выводится список новостей, с возможностью детального просомтра. Адрес страницы с детальным описанием новости выглядит так: test.ru/?detail=1. Т.е через GET запрос переменная detail передаёт значение 1 (которое является идентификатором записи в табице новостей).
Изменяем GET запрос на ?detail=1′ или ?detail=1″ . Далее пробуем передавать эти запросы серверу, т.е заходим на test.ru/?detail=1′ или на test.ru/?detail=1″.
Если при заходе на данные страницы появляется ошибка, значит сайт уязвим на SQL инъекции.
Пример ошибки, возникающей при проверке уязвимости
Возможные SQL инъекции (SQL внедрения)
1) Наиболее простые — сворачивание условия WHERE к истиностному результату при любых значениях параметров.
2) Присоединение к запросу результатов другого запроса. Делается это через оператор UNION.
3) Закомментирование части запроса.Практика. Варианты взлома сайта с уязвимостью на SQL внедрения
Итак, у нас есть уже упоминавшийся сайт test.ru. В базе хранится 4 новости, 3 из которых выводятся. Разрешение на публикацию новости зависит от парметра public (если параметр содержит значение 1, то новость публикуется).
Список новостей, разрешённых к публикации
При обращении к странице test.ru/?detail=4, которая должна выводить четвёртую новость появляется ошибка – новость не найдена.
В нашем случае новость существует, но она запрещена к публикации.Но так как мы уже проверяли сайт на уязвимость и он выдавал ошибку БД, то пробуем перебирать возможные варианты запросов.
В адресной строке плюс (+) выполняет роль пробела, так что не пугайтесьВ итоге удача улыбнулась и два запроса (первый и третий) вернули нам детальное описание четвёртой новости
Разбор примера изнутри
За получение детального описания новости отвечает блок кода:
$detail_id=$_GET[‘detail’];
$zapros=»SELECT * FROM `$table_news` WHERE `public`=’1′ AND `id`=$detail_id ORDER BY `position` DESC»;Мало того, что $detail_id получает значение без какой либо обработки, так ещё и конструкция `id`=$detail_id написана криво, лучше придерживаться `id`=’$detail_id’ (т.е сравниваемое значение писать в прямых апострофах).
Глядя на запрос, получаемый при обращении к странице через test.ru/?detail=4+OR+1
SELECT * FROM `news` WHERE `public`=’1′ AND `id`=4 OR 1 ORDER BY `position` DESC
становится не совсем ясно, почему отобразилась 4-ая новость. Дело в том, что запрос вернул все записи из таблицы новостей, отсортированные в порядке убывания сверху. И таким образом наша 4-ая новость оказалась самой первой, она же и вывелась как детальная. Т.е просто совпадение.
Разбираем запрос, сформированный при обращении через test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4 .
Тут название таблицы с новостями (в нашем случае это news) бралось логическим перебором.
Итак, выполнился запрос SELECT * FROM `news` WHERE `public`=’1′ AND `id`=4 UNION SELECT * FROM news WHERE ORDER BY `position` DESC . К нулевому результату первой части запроса (до UNION) присоединился результат второй части (после UNION), вернувшей детальное описание 4-ой новости.Защита от SQL инъекций (SQL внедрений)
Защита от взлома сводится к базовому правилу «доверяй, но проверяй». Проверять нужно всё – числа, строки, даты, данные в специальных форматах.
Числа
Для проверки переменной на числовое значение используется функция is_numeric(n);, которая вернёт true, если параметр n — число, и false в противном случае.
Так же можно не проверять значение на число, а вручную переопределить тип. Вот пример, переопределяющий значение $id, полученное от $_GET[‘id_news’] в значение целочисленного типа (в целое число):
$id=(int)$_GET[‘id_news’];Строки
Большинство взломов через SQL происходят по причине нахождения в строках «необезвреженных» кавычек, апострофов и других специальных символов. Для такого обезвреживания нужно использовать функцию addslashes($str);, которая возвращает строку $str с добавленным обратным слешем (\) перед каждым специальным символом. Данный процесс называется экранизацией.
$a=»пример текста с апострофом ‘ «;
echo addslashes($a); //будет выведено: пример текста с апострофом \’Кроме этого существуют две функции, созданные именно для экранизации строк, используемых в SQL выражениях.
Это mysql_escape_string($str); и mysql_real_escape_string($str);.Первая не учитывает кодировку соединения с БД и может быть обойдена, а вот вторая её учитывает и абсолютно безопасна. mysql_real_escape_string($str); возвращает строку $str с добавленным обратным слешем к следующим символам: \x00, \n, \r, \, ‘, » и \x1a .
Магические кавычки
Магические кавычки – эффект автоматической замены кавычки на обратный слэш (\) и кавычку при операциях ввода/вывода. В некоторых конфигурациях PHP этот параметр включён, а в некоторых нет. Для того, что бы избежать двойного экранизирования символов и заэкранизировать данные по-нормальному через mysql_real_escape_string($str);, необходимо убрать автоматические проставленные обратные слеши (если магические кавычки включены).
Проверка включённости магических кавычек для данных получаемых из GET, POST или Куков организуется через функцию get_magic_quotes_gpc(); (возвращает 1 – если магические кавычки включены, 0 – если отключены).
Если магические кавычки вкючены (т.е обратные слеши добавляеются) и такое встречается чаще, то их нужно убрать. Это делается через функцию stripslashes($str); (возвращает строку $str без обратных слешей у кавычек и прямых апострофов).
В закючении привожу код с полной экранизацией строк для записи в БД
if(get_magic_quotes_gpc()==1)
$element_title=stripslashes(trim($_POST[«element_title»]));
$element_text=stripslashes(trim($_POST[«element_text»]));
$element_date=stripslashes(trim($_POST[«element_date»]));
>
else
$element_title=trim($_POST[«element_title»]);
$element_text=trim($_POST[«element_text»]);
$element_date=trim($_POST[«element_date»]);
>$element_title=mysql_real_escape_string($element_title);
$element_text=mysql_real_escape_string($element_text);
$element_date=mysql_real_escape_string($element_date);Статья была подготовлена на основе практических навыков по защите веб-систем. Теория дело хорошее, но практика важнее и главное она работает.
SQL-инъекции для самых маленьких
Мы переходим к технической части статей про тестирование на проникновение. И начнем как всегда с внешнего пути – с эксплуатации веб уязвимостей. И стартанем мы с SQL – инъекций.
SQL-инъекция (SQLi) — это уязвимость веб-безопасности, которая позволяет злоумышленнику вмешиваться в запросы, которые приложение делает к своей базе данных. Как правило, это позволяет просматривать данные, которые он обычно не может получить. Это могут быть других пользователей, или любые другие данные, доступ к которым имеет само приложение. Во многих случаях злоумышленник может изменять или удалять эти данные, вызывая постоянные изменения в содержимом или поведении приложения.
В некоторых ситуациях злоумышленник может усилить атаку SQL-инъекции, чтобы скомпрометировать базовый сервер или другую внутреннюю инфраструктуру, или осуществить атаку типа «отказ в обслуживании».
Каковы последствия успешной атаки SQL-инъекции?
Успешная атака SQL-инъекции может привести к несанкционированному доступу к конфиденциальным данным, таким как пароли, данные кредитных карт или личная информация пользователей. Многие громкие утечки данных в последние годы стали результатом атак с использованием SQL-инъекций, что привело к ухудшению репутации и штрафам со стороны регулирующих органов. В некоторых случаях злоумышленник может получить постоянный черный ход в системы организации, что приводит к долгосрочной угрозе, которая может оставаться незамеченной в течение длительного периода времени.
Примеры SQL-инъекций
Существует широкий спектр уязвимостей, атак и методов SQL-инъекций, которые возникают в различных ситуациях. Некоторые распространенные примеры инъекций SQL включают:
- Получение скрытых данных, когда вы можете изменить SQL-запрос, чтобы вернуть дополнительные результаты.
- Подрыв логики приложения, когда можно изменить запрос, чтобы вмешаться в логику приложения.
- Атаки UNION, когда можно получить данные из разных таблиц базы данных.
- Изучение базы данных, когда можно получить информацию о версии и структуре базы данных.
- Слепая SQL-инъекция, когда результаты контролируемого вами запроса не возвращаются в ответах приложения.
Извлечение скрытых данных
Рассмотрим приложение для покупок, которое отображает товары в различных категориях. Когда пользователь нажимает на категорию «Подарки», его браузер запрашивает URL-адрес:
Это заставляет приложение выполнить SQL-запрос для получения информации о соответствующих товарах из базы данных:
SELECT * FROM products WHERE category = ‘Gifts’ AND released = 1
Этот SQL-запрос просит базу данных вернуть:
все данные (*) из таблицы продуктов где категория — «Подаркии» released = 1.
Ограничение released = 1 используется для скрытия продуктов, которые не выпущены. Для невыпущенных продуктов, предположительно, released = 0.
В приложении не реализована защита от атак SQL-инъекций, поэтому злоумышленник может построить атаку типа:
В результате получится SQL-запрос:
SELECT * FROM products WHERE category = ‘Gifts’—‘ AND released = 1
Ключевым моментом здесь является то, что последовательность двойных тире — является индикатором комментария в SQL и означает, что остальная часть запроса интерпретируется как комментарий. Это эффективно удаляет остаток запроса, так что он больше не включает AND released = 1. Это означает, что отображаются все продукты, включая еще не выпущенные.
Если пойти дальше, злоумышленник может заставить приложение отобразить все продукты в любой категории, включая категории, о которых он не знает:
В результате получается SQL-запрос:
SELECT * FROM products WHERE category = ‘Gifts’ OR 1=1—‘ AND released = 1
Модифицированный запрос вернет все товары, где
либо категория — Gifts, либо 1 равна 1. Поскольку 1=1 всегда истинно, запрос
вернет все товары.Подрыв логики приложения
Рассмотрим приложение, которое позволяет пользователям входить в систему с помощью имени пользователя и пароля. Если пользователь вводит имя пользователя wiener и пароль bluecheese, приложение проверяет учетные данные, выполняя следующий SQL-запрос:
SELECT * FROM users WHERE username = ‘wiener’ AND password = ‘bluecheese’
Если запрос возвращает данные пользователя, то вход в систему будет успешным. В противном случае он будет отклонен.
Здесь злоумышленник может войти в систему как любой пользователь без пароля, просто используя последовательность комментариев SQL — для удаления проверки пароля из пункта WHERE запроса. Например, если ввести имя пользователя administrator’— и пустой пароль, то получится следующий запрос:
SELECT * FROM users WHERE username = ‘administrator’—‘ AND password = »
Этот запрос возвращает пользователя, чье имя пользователя — administrator, и успешно регистрирует атакующего как этого пользователя.
Получение данных из других таблиц базы данных
В случаях, когда результаты SQL-запроса возвращаются в ответах приложения, злоумышленник может использовать уязвимость SQL-инъекции для получения данных из других таблиц базы данных. Для этого используется ключевое слово UNION, которое позволяет выполнить дополнительный запрос SELECT и добавить его результаты к исходному запросу.
Например, если приложение выполняет следующий запрос, содержащий пользовательский ввод «Gifts»:
SELECT name, description FROM products WHERE category = ‘Gifts’
то злоумышленник может отправить ввод:
‘ UNION SELECT username, password FROM users—.
Это заставит приложение вернуть все имена пользователей и пароли вместе с названиями и описаниями товаров.
Изучение базы данных, ну или определение версии и т. д.
После первоначальной идентификации уязвимости SQL-инъекции обычно полезно получить некоторую информацию о самой базе данных. Эта информация часто может проложить путь для дальнейшей эксплуатации.
Вы можете запросить информацию о версии базы данных. Способ, которым это делается, зависит от типа базы данных, поэтому вы можете определить тип базы данных по тому, какая техника работает. Например, в Oracle вы можете выполнить:
SELECT * FROM v$version
Вы также можете определить, какие таблицы базы данных существуют, и какие столбцы они содержат. Например, для большинства баз данных вы можете выполнить следующий запрос, чтобы получить список таблиц:
SELECT * FROM information_schema.tables
Слепые уязвимости SQL-инъекции
Многие случаи SQL-инъекций являются слепыми уязвимостями. Это означает, что приложение не возвращает результаты SQL-запроса или сведения об ошибках базы данных в своих ответах. Слепые уязвимости все еще могут быть использованы для несанкционированного доступа к данным, но соответствующие техники обычно более сложны и трудновыполнимы.
В зависимости от характера уязвимости и используемой базы данных, для эксплуатации слепых уязвимостей SQL-инъекций можно использовать следующие техники:
- Можно изменить логику запроса, чтобы вызвать заметную разницу в реакции приложения в зависимости от истинности одного условия. Это может включать в себя введение нового условия в некоторую булеву логику или условное срабатывание ошибки, такой как деление на ноль.
- Можно условно вызвать временную задержку в обработке запроса, что позволяет сделать вывод об истинности условия на основе времени, которое требуется приложению для ответа.
- Можно вызвать внеполосное сетевое взаимодействие, используя технику OAST. Эта техника является чрезвычайно мощной и работает в ситуациях, когда другие техники не работают. Часто вы можете напрямую передать данные по внеполосному каналу, например, поместив данные в DNS-поиск для домена, который вы контролируете.
В следующих статьях мы подробно разберем каждый вид SQL –инъекции.