Как я должен дезинформировать ввод базы данных в Java?

Может ли кто-нибудь указать мне хорошее руководство для начинающих по безопасному запуску SQL-запросов, сформированных частично из пользовательского ввода? Я использую Java, но руководство, ориентированное на язык, тоже прекрасное.

Желаемое поведение заключается в том, что если кто-то вводит в GUI что-то вроде

very nice;) DROP TABLE FOO;

База данных должна обрабатывать ее как литеральную строку и сохранять ее безопасно, не отбрасывая никаких таблиц.

Ответы

Ответ 1

Вы определенно хотите использовать PreparedStatement s. Они удобны. Вот пример .

Ответ 3

Как правило, вы не должны создавать конкатенацию ввода запроса, но вместо этого используйте PreparedStatement.

Это позволяет указать, в каких местах вы будете устанавливать параметры внутри своего запроса, поэтому Java позаботится о том, чтобы санировать все входы для вас.

Ответ 4

PreparedStatement? Да, конечно. Но я думаю, что есть еще один шаг: проверка ввода из пользовательского интерфейса и привязка к объектам до приближения к базе данных.

Я вижу, где привязка String в PreparedStatement может по-прежнему оставаться уязвимой для атаки SQL-инъекции:

String userInput = "Bob; DELETE FROM FOO";
String query = "SELECT * FROM FOO WHERE NAME = ?";

PreparedStatement ps = connection.prepareStatement(query);
ps.setString(1, userInput);
ps.executeQuery();

Я должен признать, что я сам не пробовал, но если это возможно, я бы сказал, что PreparedStatement необходим, но недостаточно. Валидация и привязка на стороне сервера являются ключевыми.

Я бы рекомендовал сделать это с помощью API-интерфейсов Spring.

Ответ 5

Ваш пользовательский ввод фактически должен быть "Bob'; delete from foo; select '" (или что-то вроде этого), поэтому неявные цитаты, добавленные подготовленным оператором, будут закрыты:

SELECT * FROM FOO WHERE NAME = 'Bob'; delete from foo; select ''

но если вы сделаете это, то подготовленный код оператора будет цитировать вашу цитату, чтобы вы получили фактический запрос

SELECT * FROM FOO WHERE NAME = 'Bob''; delete from foo; select '''

и ваше имя будет храниться как "Bob', delete from foo; select '" вместо запуска нескольких запросов.