Как очистить SQL без использования подготовленных операторов

Для некоторых операторов sql я не могу использовать подготовленный элемент, например:

SELECT MAX(AGE) FROM ?

Например, когда я хочу изменить таблицу. Есть ли утилита, которая санирует sql в Java? В рубине есть один.

Ответы

Ответ 1

Правильные, подготовленные параметры запроса оператора могут использоваться только там, где вы должны использовать одно значение литерала. Вы не можете использовать параметр для имени таблицы, имени столбца, списка значений или любого другого синтаксиса SQL.

Итак, вам нужно интерполировать вашу переменную приложения в строку SQL и соответствующим образом привести строку в соответствие. Используйте цитату, чтобы разграничить идентификатор имени таблицы и избежать строки цитаты, удвоив ее:

java.sql.DatabaseMetaData md = conn.getMetaData();
String q = md.getIdentifierQuoteString();
String sql = "SELECT MAX(AGE) FROM %s%s%s";
sql = String.format(sql, q, tablename.replaceAll(q, q+q), q);

Например, если ваше имя таблицы буквально table"name, а ваш кодовый символ идентификатора RDBMS ", то sql должен содержать строку типа:

SELECT MAX(AGE) FROM "table""name"

Я также согласен с комментарием @ChssPly76 - лучше всего, если ваш пользовательский ввод на самом деле не является буквальным именем таблицы, а означает, что ваш код отображает имя таблицы, которое затем интерполируется в SQL-запрос. Это дает вам больше уверенности в том, что SQL-инъекция не может произойти.

HashMap h = new HashMap<String,String>();
/* user-friendly table name maps to actual, ugly table name */
h.put("accounts", "tbl_accounts123");

userTablename = ... /* user input */
if (h.containsKey(userTablename)) {
  tablename = h.get(userTablename);
} else {
  throw ... /* Exception that user input is invalid */
}
String sql = "SELECT MAX(AGE) FROM %s";
/* we know the table names are safe because we wrote them */
sql = String.format(sql, tablename); 

Ответ 2

Невозможно. Лучшее, что вы можете сделать, это использовать String#format().

String sql = "SELECT MAX(AGE) FROM %s";
sql = String.format(sql, tablename);

Обратите внимание, что это не предотвращает риски SQL-инъекций. Если значение tablename является контролируемым пользователем/клиентом значением, вам необходимо его деактивировать с помощью String#replaceAll().

tablename = tablename.replaceAll("[^\\w]", "");

Надеюсь, что это поможет.

[Edit] Я должен добавить: НЕ используйте это для значений столбца, где вы можете использовать PreparedStatement для. Просто продолжайте использовать его обычным способом для любых значений столбца.

[Edit2] Лучше всего не позволять пользователю/клиенту вводить имя табуляции так, как он хочет, но лучше представить выпадающий список, содержащий все действительные имена таблиц (которые вы можете получить с помощью DatabaseMetaData#getCatalogs()) в пользовательском интерфейсе, поэтому что пользователь/клиент может выбрать его. Не забудьте проверить на стороне сервера, если выбор действителен, потому что можно обмануть параметры запроса.

Ответ 3

В этом случае вы можете проверить имя таблицы на список доступных таблиц, получив список таблиц из DatabaseMetaData. На самом деле, возможно, просто будет проще использовать регулярное выражение для выделения пробелов, а также некоторые зарезервированные слова sql, ";" и т.д. Из строки до использования чего-то liek String.format для создания вашего полного оператора sql.

Причина, по которой вы не можете использовать prepareStatement, заключается в том, что она, вероятно, включает имя таблицы в 'и экранирует ее как строку.