Страницы

Поиск по вопросам

среда, 17 апреля 2019 г.

Защита от SQL инъекций в jdbc java

Часто вижу утверждения, что надо использовать PreparedStatement вместо обычного Statement, чтобы защититься от sql инъекций. Как он защищает?


Ответ

Коротко, для нетерпеливых:
При использовании Statement строки запроса и значений складываются.
При использовании PreparedStatement имеется шаблон запроса и данные в него вставляются, с отражением кавычек.
Ниже подробнее с примерами.

Вступление.
Имеем такую простую таблицу с данными.
+-----------+----+--------+ | userName | id | pass | +-----------+----+--------+ | admin | 1 | admin | | user | 2 | pass | | chuchelo | 3 | elli | +-----------+----+--------+
Модель User, будет содержать имя и пароль, а так же метод логин, который спросит данные с консоли.
class UserLogin { String name; String pass;
public UserLogin() { }
public void login() { BufferedReader reader = null; try{ reader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("user name: "); name = reader.readLine();
System.out.println("pass: "); pass = reader.readLine(); } catch (IOException e) { e.printStackTrace(); }finally { if (reader != null) try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } }

Метод, который будет работать с обычным Statement
UserLogin user = new UserLogin(); user.login(); try (Connection connect = MyConnection.getConnection()){ Statement statement = connect.createStatement(); String query = "SELECT userName, id, pass FROM users WHERE userName='" + user.name + "' AND pass = '" + user.pass + "'"; System.out.println(query); ResultSet resultSet = statement.executeQuery(query);
while (resultSet.next()){ System.out.printf("User: id=%d name=%s pass=%s
", resultSet.getInt("id"), resultSet.getString("userName"), resultSet.getString("pass")); } MyConnection.closeConnect(); } catch (SQLException e) { e.printStackTrace(); }
Теперь если мы запустим этот метод и введем в консоль данные без инъекции:
user name: admin pass: admin
User: id=1 name=admin pass=admin
При этом сам запрос выглядит так:
SELECT userName, id, pass FROM users WHERE userName='admin' AND pass = 'admin'
Если допустить ошибку в имени или пароле, то данные выведены не будет.
Теперь попробуем использовать инъекцию(' or'1'='1), т.е. введем такие данные:
user name: admin' or'1'='1 pass: blabla
То мы все равно получаем результат, несмотря на то, что пароль неверный:
User: id=1 name=admin pass=admin
При этом сам запрос теперь выглядит так:
SELECT userName, id, pass FROM users WHERE userName='admin' or'1'='1' AND pass = 'blabla'
т.к. выражение or'1'='1' всегда равно true, то даже без указания пароля мы получим все данные.

Как от этого защитит PreparedStatement?
Метод который будет получать данные из базы с помощью PreparedStatement
UserLogin user = new UserLogin(); user.login(); try (Connection connect = MyConnection.getConnection()){ String query = "SELECT userName, id, pass FROM users WHERE userName=? AND pass=?"; PreparedStatement statement = connect.prepareStatement(query); statement.setString(1, user.name); statement.setString(2, user.pass); System.out.println(statement); ResultSet resultSet = statement.executeQuery();
while (resultSet.next()){ System.out.printf("User: id=%d name=%s pass=%s
", resultSet.getInt("id"), resultSet.getString("userName"), resultSet.getString("pass")); } MyConnection.closeConnect(); } catch (SQLException e) { e.printStackTrace(); }
Все тоже самое, только заменили обычный Statement на PreparedStatement. Надеюсь вы на слово поверите, что при правильных данных мы получим верный результат, если нет то вот лог в консоли:
user name: user pass: pass User: id=2 name=user pass=pass Запрос: SELECT userName, id, pass FROM users WHERE userName='user' AND pass='pass'
А теперь попробуем использовать инъекцию:
user name: user' or'1'='1 pass: inject
И ответа не получаем, потому что запрос выглядит так:
SELECT userName, id, pass FROM users WHERE userName='user\' or\'1\'=\'1' AND pass='inject'
Т.е. все кавычки были отражены слешем, инъекция не удалась.

Отличие Statement от PreparedStatement
Statement - вы должны заботиться о кавычках в запросе и ставить их там где они нужны.
PreparedStatement - вставляет значения в запрос и за счет методов setString setInt и прочих. Он сам понимает где нужны кавычки, а где нет. Соответственно все входные данных оборачивает ими.

Комментариев нет:

Отправить комментарий