Java - рядок втечі для запобігання ін'єкції SQL


146

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

Я підключив якийсь код, з яким я працював, і всі \\\\\\\\\\\функції, які змушують мене зводити очі. Якщо у когось є приклад цього, я дуже вдячний.


1
Гаразд, я прийшов до висновку, що PreparedStatements - це шлях, однак, виходячи з поточних завдань, мені потрібно діяти так, як було спочатку заплановано, і просто поставити фільтр на місце на даний момент, і як тільки поточна віха буде досягнута, я можу повернутися назад і переробляти базу даних для підготовленої заяви. У той же час, щоб підтримувати оберти, чи має хтось рішення, як ефективно уникнути перерахованих вище символів для MySQL з огляду на Java, і її система регулярних виразів є абсолютним болем, щоб визначити кількість необхідних втеч ....
Скотт Боннер

2
Не всі твердження SQL можна параметризувати, наприклад, "SET ROLE_name_name" або "LISTEN_name_name"
Neil McGuigan

1
@NeilMcGuigan Yep. Більшість драйверів також відмовляться параметризувати щось на зразок, CREATE VIEW myview AS SELECT * FROM mytable WHERE col = ?оскільки головне твердження - це DDL- заява, навіть якщо частина, яку ви намагаєтесь параметризувати, насправді DML .
SeldomNeedy

Відповіді:


249

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 перераховує всі різні методи, доступні для налаштування та отримання даних.


1
за допомогою цього методу ви можете розцінювати кожен параметр як рядок і все-таки бути безпечним? Я намагаюся знайти спосіб оновити свою існуючу архітектуру, щоб вона була безпечною, не потребуючи відновлення всього шару бази даних ...
Скотт Боннер

1
Весь dynqmic SQL - це лише рядки, тому задавати питання не варто. Я не знайомий з PrepareStatement, тому справжнє питання полягає в тому, чи створює він параметризований запит, який потім може бути виконаний за допомогою ExecuteUpdate. Якщо так, це добре. Якщо ні, то це просто приховує проблему, і у вас, можливо, немає жодної захищеної опції, крім перепроектування шару бази даних. Справа з інжекцією SQL - одна з тих речей, які ви повинні розробляти з самого початку; це не те, що можна легко додати згодом.
Кілон Кілон

2
Якщо ви вставляєте в поле INTEGER, вам потрібно скористатися 'setInt'. Так само в інших числових полях баз даних будуть використовуватися інші сетери. Я розмістив посилання на документи PreparedStatement, у яких перераховані всі типи сеттера.
Kaleb Brasee

2
Так, Cylon, PreparedStatements генерують параметризовані запити.
Kaleb Brasee

2
@Kaleb Brasee, спасибі Це добре знати. Інструменти є різними у кожному середовищі, але основним варіантом є відповідь на параметризовані запити.
Кілон Кілон

46

Єдиний спосіб запобігти ін'єкції SQL - це параметризований SQL. Просто не можливо створити розумніший фільтр, ніж люди, які хакують SQL на життя.

Тож використовуйте параметри для всіх входів, оновлень та пунктів де. Dynamic SQL - це просто відкрита двері для хакерів, яка включає динамічний SQL у збережених процедурах. Параметризувати, параметризувати, параметризувати.


11
І навіть параметризований SQL не є 100% гарантією. Але це дуже вдалий початок.
duffymo

2
@duffymo, я згоден, що ніколи нічого не є безпечним на 100%. Чи є у вас приклад інженерії SQL, яка працюватиме навіть із параметризованим SQL?
Кілон Кілон

3
@Cylon Cat: Звичайно, коли фрагмент SQL (наприклад, @WhereClause або @tableName) передається як параметр, об'єднаний у SQL та виконаний динамічно. Введення SQL відбувається, коли ви дозволяєте користувачам писати ваш код. Не має значення, захоплюєте ви їх код як параметр чи ні.
Стів Касс

16
До речі, я не знаю, чому це більше не згадується, але працювати з PreparedStatements також набагато простіше і набагато читабельніше. Це одне, мабуть, робить їх за замовчуванням для кожного програміста, який знає про них.
Edan Maor

3
Зверніть увагу, що PreparedStatements для деяких баз даних ДУЖЕ дорого створити, тому якщо вам потрібно зробити їх багато, вимірюйте обидва типи.
Thorbjørn Ravn Andersen

36

Якщо ви дійсно не можете використовувати варіант 1 захисту: підготовлені заяви (параметризовані запити) або варіант захисту 2: збережені процедури , не створюйте власний інструмент, використовуйте API безпеки OWASP Enterprise Security . З OWASP ESAPI, розміщеного в коді Google:

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

Докладніші відомості див. У розділі Запобігання ін'єкції SQL у Java та шпаргалка запобігання ін'єкцій SQL .

Зверніть особливу увагу на Варіант захисту 3: Уникнення всього вводу, що надається користувачем, який представляє проект OWASP ESAPI ).


4
ESAPI, здається, не існує на сьогоднішній день. На AWS є WAF, який може допомогти проти ін'єкцій SQL, XSS тощо. Чи є на даний момент інші альтернативи?
КрісОдні

@ChrisOdney WAF можна легко обійти. Більшість фреймворків уже реалізують власну профілактику SQL-ін'єкцій, в якій вони автоматично виходять з параметрів самостійно. Альтернативи для застарілих проектів: owasp.org/index.php/…
Javan R.

19

(Це відповідь на коментар ОП під початковим запитанням; я повністю погоджуюсь, що PreparedStatement - це інструмент для цієї роботи, а не регулярні вирази.)

Коли ви говорите \n, чи ви маєте на увазі послідовність \+ nабо фактичний символ передачі ліній? Якщо це \+ n, завдання досить просте:

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

Для того, щоб відповідати одному зворотному косому ряду у введенні, ви додаєте чотири з них у рядок регулярних виразів. Щоб покласти один зворотний проріз у висновку, ви помістите чотири з них у рядок заміни. Це передбачає, що ви створюєте регулярні вирази та заміни у вигляді літералів Java String. Якщо ви створюєте їх будь-яким іншим способом (наприклад, зчитуючи їх з файлу), вам не доведеться робити все це подвійним уникненням.

Якщо у вхідного сигналу є символ передачі рядків, і ви хочете замінити його послідовністю виходу, ви можете зробити другий пропуск над входом за допомогою цього:

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

Або, можливо, ви хочете два нахили нахилу (я не надто зрозумів у цьому):

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

1
Дякую за коментар, мені подобається, як ви зробили всіх персонажів в одному, я збирався про це менш регулярний спосіб вираження замінити всіх для кожного ... Я не впевнений, як призначити відповідь на це питання зараз . Зрештою, підготовленіЗаписи - це відповідь, але для моєї нинішньої мети ваша відповідь - це відповідь, яка мені потрібна, чи будете ви засмучені, якби я дав відповідь на одну з попередньо підготовлених відповідей заяви, чи є спосіб поділитися відповіддю між парою?
Скотт Боннер

1
Оскільки це лише тимчасовий хитрість, продовжуйте і прийміть одну з відповідей підготовленої держави.
Алан Мур

12

Підготовлені Держави - це шлях у більшості, але не у всіх випадках. Іноді ви опинитесь у ситуації, коли запит або його частина має бути побудована та збережена як рядок для подальшого використання. Перегляньте шпаргалку SQL Injection Prevention на веб-сайті OWASP для отримання більш детальної інформації та API на різних мовах програмування.


1
Шаблони OWASP переміщені до GitHub. Чит-лист SQL Injection тепер знаходиться тут: github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/…
theferrit32

10

Підготовлені заяви є найкращим рішенням, але якщо вам дійсно потрібно це зробити вручну, ви також можете використовувати StringEscapeUtilsклас з бібліотеки Apache Commons-Lang. Він має escapeSql(String)метод, яким ви можете скористатися:

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


2
Для довідки: commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/… У будь-якому випадку цей метод лише уникає цитат і, схоже, не запобігає атакам SQL Injection.
Пако Абато

11
Це було вилучено з останніх версій, тому що він уникав лише цитат
Піні Чейні

2
Цю відповідь слід видалити, оскільки це не перешкоджає введенню sql.
Яван Р.

9

Використовуючи регулярний вираз для видалення тексту, який може викликати інжекцію SQL, звучить так, що оператор SQL надсилається в базу даних, Statementа не через a PreparedStatement.

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

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


6

Якщо ви маєте справу зі застарілою системою, або у вас занадто багато місць для переходу на PreparedStatements за занадто мало часу - тобто, якщо є перешкода для використання найкращої практики, запропонованої іншими відповідями, ви можете спробувати AntiSQLFilter


5

Вам потрібен наступний код нижче. На перший погляд, це може виглядати як будь-який старий код, який я склав. Однак те, що я зробив, - це поглянути на вихідний код 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();
    }
}

2
Я думаю, що цей код є декомпільованою версією вихідного коду за вищенаведеним посиланням. Тепер в новому mysql-connector-java-xxx, то case '\u00a5'і case '\u20a9'заяві , здається , вивезений
жа

я спробував sqlmap з вашим кодом, і це не захистило мене від першої атаки `Тип: блайн-блайнд Назва: AND благ-блайнд - БУДЕ або ВИДАЛО застереження Корисне навантаження: q = 1% 'І 5430 = 5430 І'% ' = '`
shareef

Вибачте за свою роботу, але переглядав останні збережені результати сеансу .. Я зберіг коментар для майбутнього подібного ..
shareef

Ви можете використовувати org.ostermiller.utils.StringHelper.escapeSQL()або com.aoindustries.sql.SQLUtility.escapeSQL().
Мохаммед Еннаді Ел Ідріссі

1
Важливо відзначити ліцензію GPLv2 на оригінальний код, з якого скопійовано для всіх, хто стикається з цим. Я не юрист, але настійно рекомендую не використовувати цю відповідь у своєму проекті, якщо ви повністю не знаєте про наслідки включення цього ліцензованого коду.
Нік Спейпк

0

Після пошуку тестування багато рішення для запобігання sqlmap від введення sql, у випадку застарілої системи, яка не може застосовувати підготовлені статури скрізь.

java-security-cross-site-scripting-xss-and-sql-injection topic БЕШЕ РІШЕННЯ

Я спробував рішення @Richard, але в моєму випадку не вийшло. Я використав фільтр

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

параметри HTTP зі спеціальними символами (<,>, ', ...) у 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;
    }
}

Це добре java-security-cross-site-scripting-xss-and-sql-injection topic? Я намагаюся знайти рішення для застарілого додатка.
caot

0

Від: [Джерело]

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

Якщо ви використовуєте PL / SQL, ви також можете використовувати DBMS_ASSERT його, щоб очистити ваш вклад, щоб ви могли використовувати його, не турбуючись про ін'єкції SQL.

див. цю відповідь, наприклад: https://stackoverflow.com/a/21406499/1726419

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.