SQL Injection
Введение
SQL Injection — один из возможных путей взлома веб приложений, работающих с SQL базами данных (БД). Данный путь основан на внедрении вредоносного кода в исходный SQL-скрипт. Например, на взламываемом сервере баз данных злоумышленник может:
- Получить доступ к таблице, доступ к которой ограничен.
- Удалять данные из таблиц, удалять сами таблицы.
- Изменять/добавлять данные в таблицах.
Разработчик таких программ, работающих с БД, должен брать во внимание потенциальную уязвимость своих приложений и принимать меры для противодействия SQL Injections.
Суть SQL Injection
Представим, что имеется веб приложение на языке Java, работающее с пользовательскими запросами по HTTP. Реализация на самом деле не важна, внедрить SQL код можно используя неверно спроектированный интерфейс пользователя даже в консольное приложение. Часть логики данного уязвимого приложения описана в следующем примере:
String login = ...;
String password = ...;
String generatedSql = “SELECT * FROM users WHERE
(login = ‘“ + login +”’ and
password = ‘“ + password+ ”’)”;
При штатном использовании сервера на него приходят валидные и логичные данные. Например, могут прийти значения:
- login = my_login@mail.com
- password = my_password_123
В этом случае произойдёт конкатенация строк Java и финальный SQL запрос к БД будет выглядеть так:
String generatedSql = “SELECT * FROM users WHERE
(login = ‘my_login@mail.com’ and
password = ‘my_password_123’)”;
Но если сервер находится под атакой злоумышленника, то вместо логичных параметров злоумышленником могут быть отправлены на сервер следующие значения:
- login = my_login@mail.com
- password = ‘ or 1=1)#
Следовательно, будет выполнен SQL запрос с внедрением кода злоумышленника:
String generatedSql = “SELECT * FROM users WHERE
(login = ‘my_login@mail.com’ and
password = ‘’ or 1=1#’)”;
Выполнив этот скрипт, сервер БД вернет корректный ответ, несмотря на отсутствие пароля в нужном параметре. Стоит отметить, что символ решетки на конце SQL Injection нужен для экранирования (комментирования) последующих ограничений исходного SQL запроса в RDBMS MySQL.
Как уже упоминалось ранее, злоумышленник может провести не только вход в систему, но и, например, удаление данных и даже таблиц. Если уязвимость имеет место, ничего не мешает вместо параметра внедрить примерно следующую SQL программу:
Bob ‘); DROP TABLE contracts;#
Что также нанесет огромный вред атакуемой программе.
Использование Union в SQL Injection
SQL обладает возможностью использовать слово UNION и тем самым объединять несколько результатов запросов в одну таблицу. Этот способ также даёт возможность получить несанкционированный доступ к данным. Представим, что имеется HTTP сервис, отдающий пользователю новости и принимающий лишь ID новости. Внутри этого сервиса имеется следующий SQL скрипт, который отвечает за отображение новостей:
String newsId = ...;
SELECT id_news, header, body, author
FROM news
WHERE id_news = " + newsId;
В этом кейсе злоумышленник может внедрить в качестве параметра newsId следующую SQL программу:
-1 UNION SELECT 1,username, password,1 FROM admin
В результате финальный SQL запрос будет выглядеть так:
SELECT id_news, header, body, author
FROM news WHERE id_news = -1
UNION SELECT 1, username, password, 1
FROM admin
Данный поддельный запрос вернет записи, относящиеся к безопасности сервиса — список имен и паролей пользователей из таблицы admin и, в то же время, не будет выведено никаких записей из таблицы news.
SQL Injection в ORM Hibernate
Потенциальном риску SQL-атаки также подвержены ORM системы, в частности, Hibernate. Это происходит по той причине, что Hibernate имеет возможность оперировать языком HQL (Hibernate Query Language). Во время создания HQL запроса у разработчика есть возможность допустить ошибку — использовать конкатенацию строк, как показано в примере:
SessionFactory sessionFactory = ...;
String user = ...;
String password = ...;
String generatedHql = “FROM LoginInfo
WHERE userName = ”+ user +” AND password = “+ password +”;”;
List<LoginInfo> logins = sessionFactory.
getCurrentSession().
createQuery(generatedHql).list();
В этом кейсе HQL код находится под уязвимостью т.к. данный пример Java кода сконструирован опасно и предоставляет возможность внедрить пользовательский HQL код.
Способы защиты от SQL Injection
Защита с помощью интерфейсов PreparedStatement и Statement
В стандартной библиотеке Java имеются интерфейсы PreparedStatement и Statement, которые можно использовать для защиты от SQL Injection.
Основное отличие PreparedStatement от Statement в том, что интерфейс Statement не принимает никаких параметров и применяется в случае статических SQL. Statement обладает низкой производительностью в связи с тем, что кэширование реализаций таких запросов не производится.
PreparedStatement используется в случае, когда планируется использовать SQL – выражения множество раз с различными параметрами. Например, исправленный вариант приведенного выше SQL запроса будет выглядеть так:
String login = ...;
String password = ...;
String sqlTemplate= “SELECT * FROM users WHERE
(login = ? and password = ?)”;
Connection connection = ...;
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, login);
statement.setString(2, password);
Нужно отметить, что использование PreparedStatement здесь оправдано как с точки зрения безопасности, так и с точки зрения производительности:
- Касательно безопасности, идея заключается в том, что если позиция параметров явно задана, то можно абсолютно безопасно передавать SQL-запросы базе данных, исключая возможность для параметров самим стать SQL-выражениями (в том числе зловредными).
- Производительность увеличивается по той причине, что клиентским приложениям не нужно заново проводить парсинг этот PreparedStatement, если он был уже выполнен ранее. Клиентское приложение лишь производит вставку нужных параметров в тело SQL запроса. Сам PreparedStatement является многоразовым используемым шаблоном.
Защита Hibernate с помощью параметризации
Для предотвращения уязвимостей в HQL нужно использовать именованные параметры вместо конкатенации Java String. Такая возможность встроена в ORM Hibernate:
SessionFactory sessionFactory = ...;
String user = ...;
String password = ...;
// Измененный HQL запрос:
String generatedHql = “FROM LoginInfo
WHERE userName = :name AND password = :password”;
// Задание HQL параметров:
query.setParameter("name", user);
query.setParameter("password", password);
List<LoginInfo> logins = sessionFactory.
getCurrentSession().
createQuery(generatedHql).list();
Использование анализатора sqlmap
Для автоматизированного подхода в поиске SQL Injection в программном обеспечении можно использовать программу sqlmap. Этот инструмент входит в дистрибутив Kali Linux и может быть установлен на Windows (дополнительно нужен Python).
Он обладает следующими возможностями:
- Поддержка многих реализаций RDBMS: Oracle, MySQL, PostgreSQL, MSSQL, IBM DB2 и прочих.
- Поддержка многих видов SQL Injection, в том числе и boolean-based blind, time-based blind, error-based, UNION query и stacked queries.
- Поддержка прямого подключения к RDBMS.
- Работа по конкретному URL.
- Поддержка перечисления имен пользователей, хешей паролей, прав доступа, ролей и т.д.
- Многие другие возможности.
Для запуска проверки на наличие уязвимостей необходимо запустить sqlmap из консоли для сканирования:
>python sqlmap.py -u localhost/test/
В процессе работы sqlmap будет задавать различные вопросы, например, нужно ли пытаться определить наименование Web Application Firewall или даже попытаться проверить сервис на наличие XSS (Cross-Site Scripting — когда вредоносный код загружается вместе с Web-страницей). Результаты работы программы, например, найденные SQL Injections, отобразятся в консоли приложения.
Заключение
В данной статье мы раскрыли суть SQL Injection, рассмотрели некоторые общие способы защиты от SQL Injection, которые вы можете применять:
- Использование шаблонизированных, кэшируемых PreparedStatement, предотвращающих вставку сторонних пользовательских SQL программ в серверный код.
- Использование именованных параметров в HQL запросах вместо конкатенации Java строк.
- Использование автоматизации поиска уязвимостей на примере sqlmap.
Используемые ссылки:
- https://habr.com/post/148151/
- https://en.wikipedia.org/wiki/SQL_injection
- https://xakep.ru/2011/12/06/57950/
- http://sqlmap.org/
- https://kali.tools/?p=816