#java #безопасность #jdbc #preparedstatement #sql_injection
Часто вижу утверждения, что надо использовать PreparedStatement вместо обычного Statement,
чтобы защититься от sql инъекций. Как он защищает?
Ответы
Ответ 1
Коротко, для нетерпеливых: При использовании 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\n", 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\n", 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 и прочих. Он сам понимает где нужны кавычки, а где нет. Соответственно все входные данных оборачивает ими.
Комментариев нет:
Отправить комментарий