Java-эквивалент для PHP mysql_real_escape_string () - PullRequest
14 голосов
/ 19 июня 2009

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

Это чтобы избежать попыток внедрения SQL перед передачей их в Statement.execute ().

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

Apache StringEscapeUtils escapeSQL () экранирует только одинарные кавычки.

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

Два вопроса для рассмотрения:

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

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

Ответы [ 7 ]

12 голосов
/ 19 июня 2009

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

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

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

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

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

6 голосов
/ 08 июля 2011

Вот код, который достигает того, что вы ищете. Первоначально в вики-издательской вики.

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

/**
  * Mysql Utilities
  *        
  * @author Ralph Ritoch <rritoch@gmail.com>
  * @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("[a-zA-Z0-9_!@#$%^&*()-=+~.;:,\\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("[a-zA-Z0-9_!@#$%^&*()-=+~.;:,\\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)+"`";
     }

 }
5 голосов
/ 19 июня 2009

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

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

3 голосов
/ 19 июня 2009

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

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

2 голосов
/ 09 января 2012

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

0 голосов
/ 13 июня 2019

Я бы не стал доверять ничему, кроме 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();
    }
}
0 голосов
/ 19 января 2016

По словам Даниэля Шнеллера, в Java нет стандартного способа обработки mysql_real_escape_string () в PHP Я сделал цепочку метода 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("\\"", "\\\"");</p> <pre><code> state.execute("insert into extractiontext(extractedtext,extractedgroup) values('"+content+"','"+group+"')"); } catch (Exception e) { e.printStackTrace(); }

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...