Java-эквивалент для PHP mysql_real_escape_string()

Есть ли Java-эквивалент PHP mysql_real_escape_string()?

Это должно избежать попыток SQL-инъекций, прежде чем передавать их в Statement.execute().

Я знаю, что вместо этого могу использовать PreparedStatement, но пусть это будут заявления с одним выстрелом, поэтому их подготовка приведет к снижению производительности . Я уже изменил код для использования PreparedStatement, но с учетом того, как был структурирован существующий код, функция escape() заставит изменения кода намного проще просмотреть и сохранить; Я предпочитаю легко поддерживать код, если нет веской причины для дополнительной сложности. Также PreparedStatements обрабатываются по-разному с помощью базы данных, поэтому это может привести к ошибкам в базе данных, с которой мы не сталкивались раньше, требуя большего тестирования перед выпуском в производство.

Apache StringEscapeUtils escapeSQL() избегает одиночных кавычек.

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

Рассматриваются две точки:

1) Подготовленные заявления не являются панацеей и не обеспечивают 100% -ную защиту от SQL-инъекций. Некоторые драйверы баз данных создают параметризованные запросы, используя небезопасную конкатенацию строк, а не предварительную компиляцию запроса в двоичную форму. Кроме того, если ваш SQL опирается на хранимые процедуры, вам необходимо убедиться, что хранимые процедуры сами по себе не строят запросы небезопасными способами.

2). Наиболее подготовленная реализация выполнения привязывает оператор к соединению с базой данных, на котором был создан оператор. Если вы используете пул соединений с базой данных, вы должны быть осторожны с используйте подготовленную ссылку справки только с соединением, которое оно было подготовлено. Некоторые механизмы объединения реализуют это прозрачно. В противном случае вы могли бы скомпилировать подготовленные операторы или (простейшие, но дополнительные служебные), создать новый подготовленный оператор для каждого запроса.

Ответ 1

Насколько я знаю, нет "стандартного" способа сделать это.

Я настоятельно рекомендую использовать подготовленные заявления, несмотря на ваши текущие проблемы. Влияние производительности будет незначительным - у нас есть аналогичная ситуация с несколькими тысячами заявлений в секунду, причем большинство из них - однократные.

Получаемая вами безопасность должна быть взвешена намного выше, чем проблема с производительностью, которую вы еще не видели. По-моему, это четкая ситуация: "Не оптимизируйте преждевременно".

В любом случае, если вы действительно узнаете позже, что вы столкнулись с проблемами производительности, убедитесь, что подготовленные заявления действительно являются причиной тщательного профилирования, а затем поискают альтернативы. До тех пор вы должны сэкономить нервы, пытаясь добиться успеха.

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

Ответ 2

Вот код, который обеспечивает то, что вы ищете. Первоначально в вики Vnet Publishing.

https://web.archive.org/web/20131202082741/http://wiki.vnetpublishing.com/Java_Mysql_Real_Escape_String

/**
  * Mysql Utilities
  *        
  * @author Ralph Ritoch <[email protected]>
  * @copyright Ralph Ritoch 2011 ALL RIGHTS RESERVED
  * @link http://www.vnetpublishing.com
  *
  */

 package vnet.java.util;

 public class MySQLUtils {

     /**
      * Escape string to protected against SQL Injection
      *
      * You must add a single quote ' around the result of this function for data,
      * or a backtick ` around table and row identifiers. 
      * If this function returns null than the result should be changed
      * to "NULL" without any quote or backtick.
      *
      * @param link
      * @param str
      * @return
      * @throws Exception 
      */

     public static String mysql_real_escape_string(java.sql.Connection link, String str) 
           throws Exception
     {
         if (str == null) {
             return null;
         }

         if (str.replaceAll("[[email protected]#$%^&*()-=+~.;:,\\Q[\\E\\Q]\\E<>{}\\/? ]","").length() < 1) {
             return str;
         }

         String clean_string = str;
         clean_string = clean_string.replaceAll("\\\\", "\\\\\\\\");
         clean_string = clean_string.replaceAll("\\n","\\\\n");
         clean_string = clean_string.replaceAll("\\r", "\\\\r");
         clean_string = clean_string.replaceAll("\\t", "\\\\t");
         clean_string = clean_string.replaceAll("\\00", "\\\\0");
         clean_string = clean_string.replaceAll("'", "\\\\'");
         clean_string = clean_string.replaceAll("\\\"", "\\\\\"");

         if (clean_string.replaceAll("[[email protected]#$%^&*()-=+~.;:,\\Q[\\E\\Q]\\E<>{}\\/?\\\\\"' ]"
           ,"").length() < 1) 
         {
             return clean_string;
         }

         java.sql.Statement stmt = link.createStatement();
         String qry = "SELECT QUOTE('"+clean_string+"')";

         stmt.executeQuery(qry);
         java.sql.ResultSet resultSet = stmt.getResultSet();
         resultSet.first();
         String r = resultSet.getString(1);
         return r.substring(1,r.length() - 1);       
     }

     /**
      * Escape data to protected against SQL Injection
      *
      * @param link
      * @param str
      * @return
      * @throws Exception 
      */

     public static String quote(java.sql.Connection link, String str)
           throws Exception
     {
         if (str == null) {
             return "NULL";
         }
         return "'"+mysql_real_escape_string(link,str)+"'";
     }

     /**
      * Escape identifier to protected against SQL Injection
      *
      * @param link
      * @param str
      * @return
      * @throws Exception 
      */

     public static String nameQuote(java.sql.Connection link, String str)
           throws Exception
     {
         if (str == null) {
             return "NULL";
         }
         return "`"+mysql_real_escape_string(link,str)+"`";
     }

 }

Ответ 3

Не предполагайте, что PreparedStatements медленнее. Попробуйте, измерьте его, а затем оцените.

PreparedStatements всегда следует использовать в предпочтении Statement, практически без исключения, особенно когда атаки SQL-инъекций - это то, чего вы пытаетесь избежать.

Ответ 4

Единственный разумный способ избежать SQL-инъекции - использовать подготовленные/параметризованные операторы.

Например, PreparedStatement, который вы пытаетесь избежать по какой-то причине. Если вы делаете одноразовые заявления, время их подготовки должно быть незначительным ( "одноразовый" и "критический по эффективности" - это противоречие, ИМХО). Если вы делаете что-то в цикле, подготовленные заявления даже приводят к увеличению производительности.

Ответ 5

org.apache.commons.lang.StringEscapeUtils.class в commons-lang.jar может решить вашу проблему!

Ответ 6

По словам Даниэля Шнеллера, нет стандартного способа обработки PHP mysql_real_escape_string() в Java То, что я сделал, это связать метод replaceAll, чтобы обрабатывать все аспекты, которые могут потребоваться, чтобы избежать каких-либо исключений. Вот мой пример кода:

public void saveExtractedText(String group,String content) { try { content = content.replaceAll("\\", "\\\\") .replaceAll("\n","\\n") .replaceAll("\r", "\\r") .replaceAll("\t", "\\t") .replaceAll("\00", "\\0") .replaceAll("'", "\\'") .replaceAll("\\"", "\\\"");

        state.execute("insert into extractiontext(extractedtext,extractedgroup) values('"+content+"','"+group+"')");
    } catch (Exception e) {
        e.printStackTrace();

    }

код >

Ответ 7

Я бы не стал доверять ничему другому, кроме PreparedStatement, для обеспечения безопасности. Но если вам нужно иметь подобный рабочий процесс при построении запросов, вы можете использовать код ниже. Он использует PreparedStatement, работает как StringBuilder, добавляет escape-функции и отслеживает индексы параметров для вас. Это можно использовать так:

SQLBuilder sqlBuilder = new SQLBuilder("update ").append(dbName).append(".COFFEES ");
sqlBuilder.append("set SALES = ").escapeString(sales);
sqlBuilder.append(", TOTAL = ").escapeInt(total);
sqlBuilder.append("where COF_NAME = ").escapeString(coffeeName);
sqlBuilder.prepareStatement(connection).executeUpdate();

Вот код:

class SQLBuilder implements Appendable {
    private StringBuilder sqlBuilder;
    private List<Object> values = new ArrayList<>();

    public SQLBuilder() {
        sqlBuilder = new StringBuilder();
    }

    public SQLBuilder(String str)
    {
        sqlBuilder = new StringBuilder(str);
    }

    @Override
    public SQLBuilder append(CharSequence csq)
    {
        sqlBuilder.append(csq);
        return this;
    }

    @Override
    public SQLBuilder append(CharSequence csq, int start, int end)
    {
        sqlBuilder.append(csq, start, end);
        return this;
    }

    @Override
    public SQLBuilder append(char c)
    {
        sqlBuilder.append(c);
        return this;
    }

    // you can add other supported parameter types here...
    public SQLBuilder escapeString(String x)
    {
        protect(x);
        return this;
    }

    public SQLBuilder escapeInt(int x)
    {
        protect(x);
        return this;
    }

    private void escape(Object o)
    {
        sqlBuilder.append('?');
        values.add(o);
    }

    public PreparedStatement prepareStatement(Connection connection)
        throws SQLException
    {
        PreparedStatement preparedStatement =
            connection.prepareStatement(sqlBuilder.toString());
        for (int i = 0; i < values.size(); i++)
        {
            Object value = values.get(i);
            // you can add other supported parameter types here...
            if (value instanceof String)
                preparedStatement.setString(i + 1, (String) value);
            else if (value instanceof Integer)
                preparedStatement.setInt(i + 1, (Integer) value);
        }
        return preparedStatement;
    }

    @Override
    public String toString()
    {
        return "SQLBuilder: " + sqlBuilder.toString();
    }
}