Java – escape-строка для предотвращения SQL-инъекций
-
06-07-2019 - |
Вопрос
Я пытаюсь внедрить некоторую анти-sql-инъекцию в Java, и мне очень трудно работать со строковой функцией replaceAll.В конечном итоге мне нужна функция, которая преобразует любые существующие \
к \\
, любой "
к \"
, любой '
к \'
, и любой \n
к \\n
так что, когда строка оценивается MySQL, SQL-инъекции будут заблокированы.
Я подключил некоторый код, с которым работал, и все \\\\\\\\\\\
в функции у меня глаза сходят с ума.Если у кого-нибудь есть такой пример, я был бы очень признателен.
Решение
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 перечислены все различные доступные методы. для настройки и получения данных.
Другие советы
Единственный способ предотвратить внедрение SQL - это параметризованный SQL. Просто невозможно создать фильтр, который умнее людей, которые взламывают SQL на жизнь.
Так что используйте параметры для всех входных данных, обновлений и предложений where. Динамический SQL является просто открытой дверью для хакеров, и это включает динамический SQL в хранимых процедурах. Параметризация, параметризация, параметризация.
Если вы действительно не можете использовать Вариант защиты 1: подготовленные операторы (параметризованные запросы) или OWASP Enterprise Security API . Из OWASP ESAPI , размещенного в коде Google:
Не & # 8217; не пишите свои собственные меры безопасности! Изобретая колесо, когда речь заходит о разработке мер безопасности для каждого веб-приложения или веб-службы, это приводит к потере времени и огромным дырам в безопасности. Наборы инструментов OWASP Enterprise Security API (ESAPI) помогают разработчикам программного обеспечения защититься от безопасности & # 8208; связанных с этим недостатков дизайна и реализации.
Для получения дополнительной информации см. Предотвращение инъекции SQL в Java и Шпаргалка по предотвращению инъекций SQL . Р>
Обратите особое внимание на вариант защиты 3: ввод всех сторон a> в котором представлен проект OWASP ESAPI .
(Это ответ на комментарий ОП по исходному вопросу; я полностью согласен с тем, что PreparedStatement является инструментом для этой работы, а не регулярными выражениями.)
Когда вы говорите \n
, подразумеваете ли вы последовательность \
+ n
или реальный символ перевода строки? Если это <=> + <=>, задача довольно проста:
s = s.replaceAll("['\"\\\\]", "\\\\$0");
Чтобы сопоставить одну обратную косую черту во входных данных, вы помещаете четыре из них в строку регулярного выражения. Чтобы поместить один обратный слеш в вывод, вы помещаете четыре из них в строку замены. Предполагается, что вы создаете регулярные выражения и замены в форме литералов Java String. Если вы создаете их любым другим способом (например, читая их из файла), вам не нужно делать все это с двойным экранированием.
Если на входе есть символ перевода строки, и вы хотите заменить его escape-последовательностью, вы можете сделать второй проход по вводу с помощью этого:
s = s.replaceAll("\n", "\\\\n");
Или, может быть, вы хотите две обратные косые черты (я не совсем уверен в этом):
s = s.replaceAll("\n", "\\\\\\\\n");
PreparedStatements - это подход в большинстве, но не во всех случаях. Иногда вы попадаете в ситуацию, когда запрос или его часть необходимо построить и сохранить в виде строки для последующего использования. Ознакомьтесь с шпаргалкой по предотвращению инъекций SQL на Сайт OWASP для получения дополнительной информации и API на разных языках программирования.
Использование регулярного выражения для удаления текста, который может вызвать внедрение SQL, звучит так, будто оператор SQL отправляется в базу данных через Statement
, а не PreparedStatement
.
Одним из самых простых способов предотвратить внедрение SQL в первую очередь является использование <=>, который принимает данные для замены в оператор SQL с использованием заполнителей, который не использует конкатенации строк при создании оператора SQL для отправить в базу данных.
Для получения дополнительной информации Использование подготовленных заявлений из Учебники по Java - это хорошее начало.
Подготовленные операторы — лучшее решение, но если вам действительно нужно сделать это вручную, вы также можете использовать команду StringEscapeUtils
класс из библиотеки Apache Commons-Lang.Он имеет escapeSql(String)
метод, который вы можете использовать:
import org.apache.commons.lang.StringEscapeUtils;
…
String escapedSQL = StringEscapeUtils.escapeSql(unescapedSQL);
Если вы имеете дело с унаследованной системой или у вас слишком много мест, чтобы переключиться на PreparedStatement
за слишком короткое время - т. е. если есть препятствие для использования наилучшей практики, предложенной другими ответами, вы можете попробовать AntiSQLFilter
Вам нужен следующий код ниже. На первый взгляд, это может выглядеть как любой старый код, который я составил. Однако я посмотрел на исходный код 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();
}
}
После поиска множества решений для предотвращения sqlmap от внедрения sql, в случае устаревшей системы, которая не может применять подготовленные отчеты везде.
java-security-cross- тема site-scripting-xss-and-sql-инъекций БЫЛО РЕШЕНИЕ
Я попробовал решение @Richard s, но в моем случае это не сработало. я использовал фильтр
Цель этого фильтра - обернуть запрос в собственный код Обертка MyHttpRequestWrapper, которая преобразует:
параметры HTTP со специальными символами (< ;, > ;, & # 8216 ;, & # 8230;) в HTML коды через org.springframework.web.util.HtmlUtils.htmlEscape (& # 8230;) метод. Примечание: в Apache Commons есть похожая классификация: org.apache.commons.lang.StringEscapeUtils.escapeHtml (& # 8230;) SQL символы внедрения (& # 8216 ;, & # 8220 ;, & # 8230;) через класс Apache Commons org.apache.commons.lang.StringEscapeUtils.escapeSql (& # 8230;)
<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;
}
}
От: [Источник]
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;
}