Бесплатный курс по Java: от основ до продвинутого уровня
PreparedStatement в Java: защита от SQL-инъекций
Напрямую в запрос данные пользователя передавать нельзя!Это опасно. Пользователь может туда вставить любое sql выражение какое ему вздумается.
Это называется sql инъекцией. И в результате может, например, что-то изменить в базе или своровать данные.
PreparedStatement – как Statement, только безопаснее. Он добавляет методы для управления входными параметрами от пользователя.
Продемонстрируем пример SQL инъекции.
Пример программы:
import java.sql.*;
public class PrStatement {
public static void main(String[] args)
throws ClassNotFoundException, SQLException {
Class.forName(“com.mysql.cj.jdbc.Driver”);
Connection connection =
DriverManager.getConnection(
“jdbc:mysql://localhost/storage”,
“root”, “07998MSD”);
Statement statement = connection.createStatement();
//Положим переменная ниже это данные от
//пользователя например которые он ввел
//в текстовое поле на странице и которые
//в результате пришли сюда в программу.
//Эта переменная будет передаваться в sql запрос.
String userInpData = “‘LOTR’ or name=’Book1′;”;
//Ниже в запрос передается переменная userInpData
ResultSet resultSet = statement.executeQuery(
“SELECT * FROM books where name=”+ userInpData);
//вредный вариант очевидно не безопасен.
//Так как пользователь прислал сюда не просто
//название книги, например LOTR, а целую часть
//SQL запроса и в итоге то что прислал
//пользователь сложиться с тем что в методе
//executeQuery и выполниться запрос:
//SELECT * FROM books where name=’LOTR’ or name=’Book1′;.
//который выведет две книги, а не одну как было
//по задумке программистом.
System.out.println(“\nBooks:”);
while (resultSet.next()) {
int id = resultSet.getInt(“id”);
String name = resultSet.getString(“name”);
System.out.println(“\n================\n”);
System.out.println(“id: ” + id);
System.out.println(“name: ” + name);
}
System.out.println(“Closing connection and ”
+”releasing resources…”);
resultSet.close();
statement.close();
connection.close();
}
}
Вывод:
Видим, что произошла инъекция SQL запроса пользователем в SQL запрос, который в методе executeQuery. Таким образом, вывелось две книги вместо одной, как задумывал программист.
Теперь продемонстрируем пример с PreparedStatement, которыйпредотвращает SQL инъекции.
Пример программы:
import java.sql.*;
public class PrStatement {
public static void main(String[] args)
throws ClassNotFoundException, SQLException {
Class.forName(“com.mysql.cj.jdbc.Driver”);
Connection connection = DriverManager.getConnection(
“jdbc:mysql://localhost/storage”,
“root”, “07998MSD”);
String userInpData = “‘LOTR’ or name=’Book1′;”
//Запрос теперь делаем с помощью PreparedStatement
PreparedStatement pstatement =
connection.prepareStatement(
“SELECT * FROM books where name = ?”);
//В строке кода ниже первый параметр
//это порядковый номер вопросика
//в верхнем запросе, второй данные от пользователя,
//которые средствами PreparedStatement будут
//переданы в запрос на место этого вопросика
//уже безопасным образом.
pstatement.setString(1,userInpData);
//preparedstatement защищает нас от инъекций как
//бы пользователь не менял userInpData.
ResultSet presultSet = pstatement.executeQuery();
System.out.println(“\nBooks:”);
while (presultSet.next()) {
int id = presultSet.getInt(“id”);
String name = presultSet.getString(“name”);
System.out.println(“\n--------------------”);
System.out.println(“id: ” + id);
System.out.println(“name: ” + name);
}
System.out.println(“Closing connection and ”
+”releasing resources…”);
presultSet.close();
pstatement.close();
connection.close();
}
}
Вывод:
Видим, что ничего не вывелось, то есть PreparedStatement предотвратил инъекцию.