Java - escape-строка для предотвращения внедрения SQL - PullRequest
140 голосов
/ 28 ноября 2009

Я пытаюсь внедрить некоторые анти-SQL инъекции в Java, и мне очень трудно работать со строковой функцией replaceAll. В конечном итоге мне нужна функция, которая преобразует любые существующие \ в \\, любые " в \", любые ' в \' и любые \n в \\n, чтобы при появлении оценивается MySQL SQL инъекции будут заблокированы.

Я поднял какой-то код, с которым работал, и все \\\\\\\\\\\ в этой функции сводят меня с ума. Если у кого-нибудь будет такой пример, я буду очень признателен.

Ответы [ 11 ]

238 голосов
/ 28 ноября 2009

PreparedStatements - это путь, потому что они делают внедрение SQL невозможным. Вот простой пример, принимающий пользовательский ввод в качестве параметров:

public insertUser(String name, String email) {
   Connection conn = null;
   PreparedStatement stmt = null;
   try {
      conn = setupTheDatabaseConnectionSomehow();
      stmt = conn.prepareStatement("INSERT INTO person (name, email) values (?, ?)");
      stmt.setString(1, name);
      stmt.setString(2, email);
      stmt.executeUpdate();
   }
   finally {
      try {
         if (stmt != null) { stmt.close(); }
      }
      catch (Exception e) {
         // log this error
      }
      try {
         if (conn != null) { conn.close(); }
      }
      catch (Exception e) {
         // log this error
      }
   }
}

Независимо от того, какие символы есть в имени и электронном письме, эти символы будут помещены непосредственно в базу данных. Они никак не повлияют на оператор INSERT.

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

44 голосов
/ 28 ноября 2009

Единственный способ предотвратить внедрение SQL - это параметризованный SQL. Просто невозможно создать более умный фильтр, чем люди, которые зарабатывают на жизнь SQL.

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

34 голосов
/ 28 ноября 2009

Если вы действительно не можете использовать Вариант защиты 1: подготовленные операторы (параметризованные запросы) или Вариант защиты 2: хранимые процедуры , не создавайте свой собственный инструмент, используйте OWASP Enterprise Security API . Из OWASP ESAPI , размещенного в Google Code:

Не пишите свои собственные меры безопасности! Изобретая колесо, когда речь заходит о разработке мер безопасности для каждого веб-приложения или веб-службы, это приводит к потере времени и огромным дырам в безопасности. Наборы инструментов OWASP Enterprise Security API (ESAPI) помогают разработчикам программного обеспечения защититься от недостатков проектирования и реализации, связанных с безопасностью.

Подробнее см. В Предотвращение внедрения SQL в Java и Шпаргалка по предотвращению внедрения SQL .

Обратите особое внимание на Вариант защиты 3: экранирование всех вводимых пользователем данных , представляющих проект OWASP ESAPI ).

19 голосов
/ 01 декабря 2009

(Это ответ на комментарий ОП по исходному вопросу; я полностью согласен с тем, что PreparedStatement является инструментом для этой работы, а не регулярными выражениями.)

Когда вы говорите \n, вы имеете в виду последовательность \ + n или фактический символ перевода строки? Если это \ + n, задача довольно проста:

s = s.replaceAll("['\"\\\\]", "\\\\$0");

Чтобы соответствовать одному обратному слешу во входных данных, вы помещаете четыре из них в строку регулярного выражения. Чтобы поместить один обратный слеш в вывод, вы помещаете четыре из них в строку замены. Предполагается, что вы создаете регулярные выражения и замены в форме литералов Java String. Если вы создаете их любым другим способом (например, читая их из файла), вам не нужно делать все это с двойным экранированием.

Если у вас есть символ перевода строки на входе, и вы хотите заменить его на escape-последовательность, вы можете сделать второй проход по вводу с помощью этого:

s = s.replaceAll("\n", "\\\\n");

Или, может быть, вы хотите две обратные косые черты (я не совсем уверен в этом):

s = s.replaceAll("\n", "\\\\\\\\n");
13 голосов
/ 11 января 2011

PreparedStatements - это путь в большинстве, но не во всех случаях. Иногда вы попадаете в ситуацию, когда запрос или его часть необходимо построить и сохранить в виде строки для последующего использования. Посетите Шпаргалку по предотвращению инъекций SQL на сайте OWASP для получения дополнительной информации и API на разных языках программирования.

9 голосов
/ 28 ноября 2009

Использование регулярного выражения для удаления текста, который может вызвать внедрение SQL, звучит так, будто оператор SQL отправляется в базу данных через Statement, а не PreparedStatement .

Один из самых простых способов предотвратить внедрение SQL в первую очередь - это использовать PreparedStatement, который принимает данные для замены в оператор SQL с использованием заполнителей, который не использует конкатенации строк при создании оператора SQL для отправить в базу данных.

Для получения дополнительной информации, Использование подготовленных операторов из Учебники по Java будут хорошим началом для начала.

8 голосов
/ 13 июля 2016

Подготовленные операторы являются лучшим решением, но если вам действительно нужно сделать это вручную, вы также можете использовать класс StringEscapeUtils из библиотеки Apache Commons-Lang. Он имеет метод escapeSql(String), который вы можете использовать:

import org.apache.commons.lang.StringEscapeUtils; … String escapedSQL = StringEscapeUtils.escapeSql(unescapedSQL);

6 голосов
/ 07 января 2016

Вам нужен следующий код ниже. На первый взгляд, это может выглядеть как любой старый код, который я составил. Тем не менее, я посмотрел на исходный код http://grepcode.com/file/repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.31/com/mysql/jdbc/PreparedStatement.java. Затем я внимательно просмотрел код setString (int parameterIndex, String x), чтобы найти символы, которые он экранировал, и настроил его для себя класс, так что он может быть использован для целей, которые вам нужны. В конце концов, если это список символов, которые Oracle избегает, то знание этого действительно утешительно с точки зрения безопасности. Может быть, Oracle нужен толчок, чтобы добавить метод, похожий на этот, для следующего основного выпуска Java.

public class SQLInjectionEscaper {

    public static String escapeString(String x, boolean escapeDoubleQuotes) {
        StringBuilder sBuilder = new StringBuilder(x.length() * 11/10);

        int stringLength = x.length();

        for (int i = 0; i < stringLength; ++i) {
            char c = x.charAt(i);

            switch (c) {
            case 0: /* Must be escaped for 'mysql' */
                sBuilder.append('\\');
                sBuilder.append('0');

                break;

            case '\n': /* Must be escaped for logs */
                sBuilder.append('\\');
                sBuilder.append('n');

                break;

            case '\r':
                sBuilder.append('\\');
                sBuilder.append('r');

                break;

            case '\\':
                sBuilder.append('\\');
                sBuilder.append('\\');

                break;

            case '\'':
                sBuilder.append('\\');
                sBuilder.append('\'');

                break;

            case '"': /* Better safe than sorry */
                if (escapeDoubleQuotes) {
                    sBuilder.append('\\');
                }

                sBuilder.append('"');

                break;

            case '\032': /* This gives problems on Win32 */
                sBuilder.append('\\');
                sBuilder.append('Z');

                break;

            case '\u00a5':
            case '\u20a9':
                // escape characters interpreted as backslash by mysql
                // fall through

            default:
                sBuilder.append(c);
            }
        }

        return sBuilder.toString();
    }
}
6 голосов
/ 28 ноября 2009

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

0 голосов
/ 17 мая 2017

From: [Источник]

public String MysqlRealScapeString(String str){
  String data = null;
  if (str != null && str.length() > 0) {
    str = str.replace("\\", "\\\\");
    str = str.replace("'", "\\'");
    str = str.replace("\0", "\\0");
    str = str.replace("\n", "\\n");
    str = str.replace("\r", "\\r");
    str = str.replace("\"", "\\\"");
    str = str.replace("\\x1a", "\\Z");
    data = str;
  }
return data;

}

...