Автор оригинала: Pankaj Kumar.
Что такое SQL-инъекция?
Инъекция SQL является одной из 10 лучших уязвимостей веб-приложений. Проще говоря, SQL-инъекция означает ввод/вставку SQL-кода в запрос с помощью введенных пользователем данных. Это может произойти в любых приложениях, использующих реляционные базы данных, такие как Oracle, MySQL, PostgreSQL и SQL Server.
Чтобы выполнить SQL-инъекцию, злоумышленник сначала пытается найти в приложении место, куда он может встроить SQL-код вместе с данными. Это может быть страница входа любого веб-приложения или любого другого места. Поэтому, когда приложение получает данные, встроенные в SQL-код, SQL-код будет выполнен вместе с запросом приложения.
Влияние SQL-инъекции
- Злоумышленник может получить несанкционированный доступ к вашему приложению и украсть данные.
- Они могут изменять, удалять данные в вашей базе данных и отключать ваше приложение.
- Хакер также может получить контроль над системой, в которой работает сервер базы данных, выполнив системные команды, относящиеся к конкретной базе данных.
Как Работает SQL-Инъекция?
Предположим, у нас есть таблица базы данных с именем tbluser , в которой хранятся данные пользователей приложений. Идентификатор пользователя является основным столбцом таблицы. У нас есть функциональность в приложении, которая позволяет вам получать информацию через идентификатор пользователя. Значение идентификатора пользователя получено из запроса пользователя.
Давайте взглянем на приведенный ниже пример кода.
String userId = {get data from end user};
String sqlQuery = "select * from tbluser where userId = " + userId;
1. Допустимый Пользовательский Ввод
Когда приведенный выше запрос выполняется с действительными данными, т. е. значением идентификатора пользователя 132, он будет выглядеть следующим образом.
Входные данные: 132
Выполненный запрос: выберите * из tbluser, где
Результат: Запрос вернет данные пользователя, имеющего идентификатор пользователя 132. В этом случае никакой SQL-инъекции не происходит.
2. Хакерский Ввод Данных Пользователем
Хакер может изменять запросы пользователей с помощью таких инструментов, как Postman, cURL и т. Д. для отправки SQL-кода в виде данных и таким образом в обход любых проверок на стороне пользовательского интерфейса.
Входные данные: 2 или
Выполненный запрос: выберите * из tbluser, где или
Результат: Теперь в приведенном выше запросе есть два условия с SQL ИЛИ выражением.
- Идентификатор пользователя=2 : Эта часть будет соответствовать строкам таблицы со значением идентификатора пользователя “2”.
- 1=1 : Эта часть всегда будет оцениваться как истинная. Таким образом, запрос вернет все строки таблицы.
Типы SQL-инъекций
Давайте рассмотрим четыре типа SQL-инъекций.
1. SQL-инъекция на основе логических значений
Приведенный выше пример представляет собой случай SQL-инъекции на основе логических значений. Он использует логическое выражение, которое принимает значение true или false. Его можно использовать для получения дополнительной информации из базы данных. Например;
Входные данные: 2 или
SQL-запрос: выберите имя, фамилию из tbl_employee, где EmpID= 2 или
2. Инъекция SQL На основе Объединения
Оператор SQL union объединяет данные из двух разных запросов с одинаковым количеством столбцов. В этом случае оператор объединения используется для получения данных из других таблиц.
Входные данные: 2. выберите имя пользователя, пароль от tbluser
Запрос: Выберите имя, фамилию из tbl_employee, где EmpID= 2. Выберите имя пользователя, пароль из tbluser
Используя SQL-инъекцию на основе объединения, злоумышленник может получить учетные данные пользователя.
3. Инъекция SQL на основе времени
В SQL-инъекции на основе времени в запрос вводятся специальные функции , которые могут приостанавливать выполнение на определенное время. Эта атака замедляет работу сервера базы данных. Это может привести к сбою в работе вашего приложения, повлияв на производительность сервера баз данных. Например, В MySQL:
Входные данные: 2 + СПЯЩИЙ РЕЖИМ(5)
Запрос: выберите emp_id, имя, фамилию из tbl_employee, где + SLEEP(5)
В приведенном выше примере выполнение запроса будет приостановлено на 5 секунд.
4. Инъекция SQL на основе ошибок
В этом варианте злоумышленник пытается получить такую информацию, как код ошибки и сообщение из базы данных. Злоумышленник вводит SQL, которые синтаксически неверны, поэтому сервер базы данных вернет код ошибки и сообщения, которые можно использовать для получения информации о базе данных и системе.
Пример внедрения Java SQL
Мы будем использовать простое веб-приложение Java для демонстрации внедрения SQL. У нас есть Login.html , которая представляет собой базовую страницу входа в систему, которая принимает имя пользователя и пароль от пользователя и отправляет их в LoginServlet .
Сервер входа в систему получает имя пользователя и пароль из запроса и проверяет их на соответствие значениям базы данных. Если аутентификация прошла успешно, сервлет перенаправляет пользователя на домашнюю страницу, в противном случае он вернет ошибку.
Login.html Код :
Sql Injection Demo
LoginServlet.java Код :
package com.journaldev.examples;
import java.io.IOException;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (Exception e) {}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
boolean success = false;
String username = request.getParameter("username");
String password = request.getParameter("password");
// Unsafe query which uses string concatenation
String query = "select * from tbluser where username='" + username + "' and password = '" + password + "'";
Connection conn = null;
Statement stmt = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/user", "root", "root");
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);
if (rs.next()) {
// Login Successful if match is found
success = true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
stmt.close();
conn.close();
} catch (Exception e) {}
}
if (success) {
response.sendRedirect("home.html");
} else {
response.sendRedirect("login.html?error=1");
}
}
}
Запросы к базе данных [MySQL]:
create database user;
create table tbluser(username varchar(32) primary key, password varchar(32));
insert into tbluser (username,password) values ('john','secret');
insert into tbluser (username,password) values ('mike','pass10');
1. При вводе действительного имени пользователя и пароля со страницы входа в систему
Введите имя пользователя : джон
Введите имя пользователя : секретно
Запрос : выберите * из tbluser, где и
Результат : Имя пользователя и пароль существуют в базе данных, поэтому аутентификация прошла успешно. Пользователь будет перенаправлен на домашнюю страницу.
2. Получение несанкционированного доступа к системе с помощью SQL-инъекции
Введите имя пользователя : фиктивный
Введите пароль : ‘или ‘1’=’1
Запрос : выберите * из tbluser, где и пароль = ” или “1”=”1″
Результат : Введенные имя пользователя и пароль не существуют в базе данных, но аутентификация прошла успешно. Почему?
Это связано с внедрением SQL, так как мы ввели ‘ или ‘1’=’1 в качестве пароля. В запросе есть 3 условия.
- имя пользователя=’фиктивный’ : Это будет оценено как ложное, так как в таблице нет пользователя с фиктивным именем пользователя.
- пароль = “ : Это будет оценено как ложь, так как в таблице нет пустого пароля.
- ‘1’=’1′ : Он будет оценен как true, так как это статическое сравнение строк.
Теперь, объединив все 3 условия, т. е. false и false или true => Конечный результат будет true .
В приведенном выше сценарии мы использовали логическое выражение для выполнения SQL-инъекции. Есть несколько других способов сделать SQL-инъекцию. В следующем разделе мы рассмотрим способы предотвращения внедрения SQL в наше Java-приложение.
Предотвращение внедрения SQL в Java-код
Самое простое решение-использовать PreparedStatement вместо Оператора для выполнения запроса.
Вместо объединения имени пользователя и пароля в запросе мы предоставляем их для запроса с помощью методов настройки Preparedstatement.
Теперь значение имени пользователя и пароля, полученные из запроса, обрабатываются только как данные, поэтому никаких SQL-инъекций не произойдет.
Давайте посмотрим на измененный код сервлета.
String query = "select * from tbluser where username=? and password = ?";
Connection conn = null;
PreparedStatement stmt = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/user", "root", "root");
stmt = conn.prepareStatement(query);
stmt.setString(1, username);
stmt.setString(2, password);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
// Login Successful if match is found
success = true;
}
rs.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
stmt.close();
conn.close();
} catch (Exception e) {
}
}
Давайте разберемся, что происходит в данном случае.
Запрос : выберите * из tbluser, где имя пользователя = ? и пароль = ?
Знак вопроса (?) в приведенном выше запросе называется позиционным параметром. В приведенном выше запросе есть 2 позиционных параметра. Мы не объединяем имя пользователя и пароль для запроса. Мы используем методы, доступные в Подготовленном заявлении , для предоставления пользовательского ввода.
Мы установили первый параметр с помощью stmt.setString(1, имя пользователя) , а второй параметр с помощью stmt.setString(2, пароль) . Базовый API JDBC заботится об очистке значений, чтобы избежать внедрения SQL.
Рекомендации по предотвращению внедрения SQL
- Проверьте данные, прежде чем использовать их в запросе.
- Не используйте общие слова в качестве имени таблицы или столбца. Например, многие приложения используют tbluser или tblaccount для хранения пользовательских данных. Электронная почта, имя, фамилия-это общие имена столбцов.
- Не объединяйте данные напрямую ( полученные в качестве пользовательского ввода) для создания SQL-запросов.
- Используйте такие фреймворки, как Спящий режим и Spring Data JPA для уровня данных приложения.
- Используйте позиционные параметры в запросе. Если вы используете обычный JDBC , то используйте PreparedStatement для выполнения запроса.
- Ограничьте доступ приложения к базе данных с помощью разрешений и разрешений.
- Не возвращайте чувствительный код ошибки и сообщение конечному пользователю.
- Сделайте надлежащий обзор кода, чтобы ни один разработчик случайно не написал небезопасный SQL-код.
- Используйте такие инструменты, как SQLMap , чтобы находить и устранять уязвимости SQL – инъекций в вашем приложении.
Это все для инъекции Java SQL, я надеюсь, что здесь не пропущено ничего важного.
Вы можете скачать пример проекта веб-приложения java по ссылке ниже.