Java - escape-строка для предотвращения SQL-инъекций

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

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

Ответ 1

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 перечислены все различные методы, доступные для настройки и получения данных.

Ответ 2

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

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

Ответ 3

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

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

Подробнее см. Предотвращение внедрения SQL в Java и SQL Injection Предотвращение чит-листа.

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

Ответ 4

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

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

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

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

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

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

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

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

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

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

Ответ 8

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

Ответ 9

Ниже приведен следующий код. С первого взгляда это может выглядеть как старый код, который я составил. Тем не менее, я сделал это, посмотрев исходный код 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();
    }
}

Ответ 10

После поиска тестового решения для предотвращения sqlmap из SQL-инъекции, в случае устаревшей системы, которая не может применять подготовленные записи каждый раз.

тема java-security-cross-site-scripting-xss-and-sql-injection БЫЛО РЕШЕНИЕ

я попробовал решение @Richard, но не работал в моем случае. Я использовал фильтр

Цель этого фильтра - обернуть запрос в собственную кодировку wrapper MyHttpRequestWrapper, который преобразует:

параметры HTTP со специальными символами (<, > , ',...) в HTML коды через org.springframework.web.util.HtmlUtils.htmlEscape(...) метод. Примечание. В Apache Commons есть аналогичный класс: org.apache.commons.lang.StringEscapeUtils.escapeHtml(...) SQL (', ",...) через класс Apache Commons org.apache.commons.lang.StringEscapeUtils.escapeSql(...)

<filter>
<filter-name>RequestWrappingFilter</filter-name>
<filter-class>com.huo.filter.RequestWrappingFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>RequestWrappingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>




package com.huo.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletReponse;
import javax.servlet.http.HttpServletRequest;

public class RequestWrappingFilter implements Filter{

    public void doFilter(ServletRequest req, ServletReponse res, FilterChain chain) throws IOException, ServletException{
        chain.doFilter(new MyHttpRequestWrapper(req), res);
    }

    public void init(FilterConfig config) throws ServletException{
    }

    public void destroy() throws ServletException{
    }
}




package com.huo.filter;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.lang.StringEscapeUtils;

public class MyHttpRequestWrapper extends HttpServletRequestWrapper{
    private Map<String, String[]> escapedParametersValuesMap = new HashMap<String, String[]>();

    public MyHttpRequestWrapper(HttpServletRequest req){
        super(req);
    }

    @Override
    public String getParameter(String name){
        String[] escapedParameterValues = escapedParametersValuesMap.get(name);
        String escapedParameterValue = null; 
        if(escapedParameterValues!=null){
            escapedParameterValue = escapedParameterValues[0];
        }else{
            String parameterValue = super.getParameter(name);

            // HTML transformation characters
            escapedParameterValue = org.springframework.web.util.HtmlUtils.htmlEscape(parameterValue);

            // SQL injection characters
            escapedParameterValue = StringEscapeUtils.escapeSql(escapedParameterValue);

            escapedParametersValuesMap.put(name, new String[]{escapedParameterValue});
        }//end-else

        return escapedParameterValue;
    }

    @Override
    public String[] getParameterValues(String name){
        String[] escapedParameterValues = escapedParametersValuesMap.get(name);
        if(escapedParameterValues==null){
            String[] parametersValues = super.getParameterValues(name);
            escapedParameterValue = new String[parametersValues.length];

            // 
            for(int i=0; i<parametersValues.length; i++){
                String parameterValue = parametersValues[i];
                String escapedParameterValue = parameterValue;

                // HTML transformation characters
                escapedParameterValue = org.springframework.web.util.HtmlUtils.htmlEscape(parameterValue);

                // SQL injection characters
                escapedParameterValue = StringEscapeUtils.escapeSql(escapedParameterValue);

                escapedParameterValues[i] = escapedParameterValue;
            }//end-for

            escapedParametersValuesMap.put(name, escapedParameterValues);
        }//end-else

        return escapedParameterValues;
    }
}

Ответ 11

From: [Source]

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;

}

Ответ 12

Я думаю, что escapeSql из StringEscapeUtils может сделать эту работу. Тем не менее, чтобы попробовать это, но выглядит как заслуживающий доверия вариант.