Регулярное выражение Java для предотвращения SQL-инъекций [дубликат]

A NullReferenceException бросается, когда мы пытаемся получить доступ к свойствам нулевого объекта или когда значение строки становится пустым, и мы пытаемся получить доступ к строковым методам.

Например:

  1. При использовании метода string пустой строки:
    string str = string.Empty;
    str.ToLower(); // throw null reference exception
    
  2. Когда свойство нулевого объекта доступно:
    Public Class Person {
        public string Name { get; set; }
    }
    Person objPerson;
    objPerson.Name  /// throw Null refernce Exception 
    
134
задан Tim Pietzcker 28 November 2009 в 19:45
поделиться

11 ответов

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

227
ответ дан Kaleb Brasee 24 August 2018 в 02:53
поделиться
  • 1
    с помощью этого метода вы можете обрабатывать каждый параметр как строку и по-прежнему быть в безопасности? Я пытаюсь найти способ обновить существующую архитектуру, чтобы быть в безопасности, не перестраивая весь слой базы данных ... – Scott Bonner 28 November 2009 в 17:34
  • 2
    Все dynqmic SQL - это просто строки, так что это не вопрос. Я не знаком с PrepareStatement, поэтому реальный вопрос заключается в том, генерирует ли он параметризованный запрос, который затем может быть выполнен с помощью ExecuteUpdate. Если да, это хорошо. Если нет, то это просто скрывает проблему, и у вас может не быть какой-либо защищенной опции, кроме перепроектирования уровня базы данных. Работа с SQL-инъекцией - одна из тех вещей, с которой вы должны проектировать с самого начала; это не то, что вы можете легко добавить позже. – Cylon Cat 28 November 2009 в 17:52
  • 3
    Если вы вставляете в поле INTEGER, вам нужно использовать 'setInt'. Аналогично, в других числовых областях базы данных будут использоваться другие сеттеры. Я разместил ссылку на документы PreparedStatement, в которых перечислены все типы сеттеров. – Kaleb Brasee 28 November 2009 в 17:52
  • 4
    Да Cylon, PreparedStatements генерируют параметризованные запросы. – Kaleb Brasee 28 November 2009 в 17:54
  • 5
    @Kaleb Brasee, спасибо. Это хорошо знать. Инструменты в разных средах различны, но основным вопросом является переход к параметризованным запросам. – Cylon Cat 28 November 2009 в 18:01

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

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

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

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

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

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

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

s = s.replaceAll("\n", "\\\\\\\\n");
17
ответ дан Alan Moore 24 August 2018 в 02:53
поделиться
  • 1
    Спасибо за комментарий, мне нравится, как вы делали все персонажи в одном, я собирался сделать это менее регулярным способом выражения для замены всех для каждого ... Я не уверен, как назначить ответ на этот вопрос сейчас , В конечном счете PreparedStatements - это ответ, но для моей текущей цели ваш ответ - это ответ, который мне нужен, вы были бы расстроены, если бы я дал ответ на один из ответов более раннего подготовленного заявления или есть способ поделиться с ним ответами между парой? – Scott Bonner 2 December 2009 в 14:30
  • 2
    Поскольку это всего лишь временный kludge, продолжайте и примите одно из ответов PreparedStatement. – Alan Moore 2 December 2009 в 23:20

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

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;

}

0
ответ дан Billcountry Mwaniki 24 August 2018 в 02:53
поделиться

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

6
ответ дан Bozho 24 August 2018 в 02:53
поделиться

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

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

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

9
ответ дан coobird 24 August 2018 в 02:53
поделиться

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

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

45
ответ дан Cylon Cat 24 August 2018 в 02:53
поделиться
  • 1
    И даже параметризованный SQL не является 100% гарантией. Но это очень хорошее начало. – duffymo 28 November 2009 в 17:37
  • 2
    @duffymo, я согласен, что ничто никогда не будет на 100% безопасным. У вас есть пример внедрения SQL, который будет работать даже с параметризованным SQL? – Cylon Cat 28 November 2009 в 17:42
  • 3
    @Cylon Cat: Конечно, когда кусок SQL (например, @WhereClause или @tableName) передается как параметр, объединенный в SQL, и выполняется динамически. SQL-инъекция происходит, когда вы позволяете пользователям писать ваш код. Неважно, записываете ли вы свой код в качестве параметра или нет. – Steve Kass 28 November 2009 в 18:46
  • 4
    Кстати, я не знаю, почему это больше не упоминается, но работа с PreparedStatements также значительно облегчается much и much более читабельна. Это само по себе делает их дефолтом для каждого программиста, который знает о них. – Edan Maor 28 November 2009 в 19:54
  • 5
    Обратите внимание, что PreparedStatements для некоторых баз данных ОЧЕНЬ дорого для создания, поэтому, если вам нужно сделать много их, измерьте оба типа. – Thorbjørn Ravn Andersen 29 November 2009 в 09:44

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

13
ответ дан jonnieZG 24 August 2018 в 02:53
поделиться

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

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

8
ответ дан ktbiz 24 August 2018 в 02:53
поделиться

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

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

Подробнее см. В Предотвращение внедрения SQL в Java и Шифрование для предотвращения инъекций SQL .

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

33
ответ дан Pascal Thivent 24 August 2018 в 02:53
поделиться
  • 1
    ESAPI кажется несуществующим на сегодняшний день. На AWS есть WAF, который может помочь в SQL-инъекции, XSS и т. Д. Есть ли другие альтернативы на данный момент? – ChrisOdney 31 May 2017 в 08:19

Ниже приведен следующий код. С первого взгляда это может выглядеть как старый код, который я составил. Тем не менее, я посмотрел исходный код для 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
ответ дан Richard 24 August 2018 в 02:53
поделиться
  • 1
    Я думаю, что этот код является декомпилированной версией исходного кода в приведенной выше ссылке. Теперь в новее mysql-connector-java-xxx команды case '\u00a5' и case '\u20a9' кажутся удаленными – zhy 18 April 2016 в 11:22
  • 2
    Спасибо за обновление zhy. – Richard 20 August 2016 в 22:26
  • 3
    я попробовал sqlmap с вашим кодом, и он не защитил меня от атаки frist. Тип: boolean based blind Название: AND boolean-based blind - WHERE или HAVING clause Полезная нагрузка: q = 1% 'AND 5430 = 5430 AND'% ' = '' – shareef 9 September 2016 в 11:46
  • 4
    Извините, что он работает, но просматривал последние сохраненные результаты сеанса. Я сохранил комментарий для будущего подобного. – shareef 9 September 2016 в 12:52
  • 5
    Важно отметить, что лицензия GPLv2 на исходный код была скопирована для всех, кто сталкивался с этим. Я не юрист, но я бы настоятельно рекомендовал не использовать этот ответ в вашем проекте, если вы не полностью осведомлены о последствиях включения этого лицензионного кода. – Nick Spacek 18 April 2017 в 11:52

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

java-security-cross-site-scripting- xss-and-sql-injection topic БЫЛО РЕШЕНИЕ

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

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

параметры HTTP со специальными символами (& lt; ,>, ', ...) в HTML-коды через метод org.springframework.web.util.HtmlUtils.htmlEscape (...). Примечание. В Apache Commons есть аналогичный класс: org.apache.commons.lang.StringEscapeUtils.escapeHtml (...) символы ввода SQL (', ", ...) через Apache Commons classe 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;
    }
}
0
ответ дан shareef 24 August 2018 в 02:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: