SQL-инъекции#
SQL-инъекция (sqli) — это уязвимость безопасности, которая позволяет злоумышленнику вмешиваться в запросы к базе данных приложения, внедряя злонамеренный SQL-код через входные данные, меняя исходный SQL-запрос.
Проблема возникает, когда приложение «слепо» объединяет пользовательские данные с кодом SQL-запроса, не проверяя и не обрабатывая их.
Пример инъекции#
Исходный запрос:
SELECT * FROM users WHERE username = '[user_input_1]' AND password = '[user_input_2]'
Злоумышленник вводит в поле username специальную строку: OR 1=1 --. Поле password он может оставить пустым или заполнить чем угодно.
Злонамеренный ввод:
user_input_1: admin“ or 1=1 –
user_input_2: [любой]
Результат:
SELECT * FROM users WHERE username = 'admin' or 1=1 --' AND password = 'text'
Чем опасны SQL-инъекции?#
Злоумышленник может:
Обойти аутентификацию.
Получить несанкционированный доступ к данным: читать, копировать, скачивать всю базу данных.
Изменять и удалять данные: удалять, изменять, добавлять записи.
Выполнять команды на сервере БД: в некоторых случаях атака может привести к полному захвату сервера.
Методы защиты#
Подготовленные выражения (Prepared Statement) - наиболее эффективный метод защиты. Это механизм выполнения SQL-запросов, при котором шаблон SQL-запроса и данные передаются отдельно. Это полностью исключает возможность SQL-инъекций. Предпочтительнее использовать именно этот метод защиты.
val svName = "admin"
val svPass = "qwerty1234"
SQL(s"SELECT * FROM users WHERE username = {sUserName} AND password = {sPassHash}")
.on("sUserName" -> svName, "sPassHash" -> svPass)
Использование функций экранирования. Если использовать подготовленные выражения не представляется возможным, необходимо воспользоваться функциями экранирования, они «обезвреживают» специальные символы:
escapeString(input: String, quoteValue: Boolean): String - для экранирования строки. Экранирует символы одинарной кавычки (“) в строке. Если quoteValue = true, заключит значение в одинарные кавычки.
val sUserInput = SqlEscape.escapeString("' union select id from Btk_User --", false)
SQL(s"SELECT id FROM Bs_Goods WHERE sName = '$sUserInput' ")
escapeNumber(input: String): String - для экранирования числа. Удаляет спецсимволы („;“, „–“, „/“, „/“, пробелы).
val sIdGood = SqlEscape.escapeNumber("1 OR 1=1 --")
SQL(s"""SELECT id FROM Bs_Goods WHERE id = $sIdGood""")
escapeName(input: String): String - для экранирования имен сущностей, например имена таблиц и колонок. Экранирует одинарные кавычки („), удаляет спецсимволы
';', '--', '/*', '*/', '(', ')'.escapeDate(input: String): String - для экранирования даты. Экранирует символы одинарной кавычки („).
escapeJson(input: String): String - для экранирования json. Экранирует символы одинарной кавычки („).
escapeBoolean(input: String): String - для экранирования булевого значения. Если строка не равна «true», вернет «false».