Java – escape string para evitar injeção de SQL

Eu estou tentando colocar um pouco de injeção de sql no lugar em java e estou achando muito difícil trabalhar com a function de string “replaceAll”. Em última análise, eu preciso de uma function que converta qualquer \ to \\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

Eu levantei algum código com o qual eu estava trabalhando e todas as funções estão fazendo meus olhos enlouquecerem. Se alguém tiver um exemplo disso, eu agradeceria muito.

As PreparedStatements são o caminho a percorrer, porque tornam a injeção de SQL impossível. Aqui está um exemplo simples tomando a input do usuário como os parâmetros:

 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 } } } 

Não importa quais caracteres estejam no nome e no email, esses caracteres serão colocados diretamente no database. Eles não afetarão a instrução INSERT de forma alguma.

Existem diferentes methods de configuração para tipos de dados diferentes – qual deles você usa depende de quais são os campos do seu database. Por exemplo, se você tiver uma coluna INTEGER no database, deverá usar um método setInt . A documentação do PreparedStatement lista todos os diferentes methods disponíveis para configuração e obtenção de dados.

A única maneira de impedir a injeção de SQL é com o SQL parametrizado. Simplesmente não é possível criar um filtro mais inteligente do que as pessoas que hackam o SQL para ganhar a vida.

Portanto, use parâmetros para todas as inputs, atualizações e cláusulas where. O SQL dynamic é simplesmente uma porta aberta para hackers, e isso inclui SQL dynamic em stored procedures. Parametrizar, parametrizar, parametrizar.

Se realmente você não puder usar a Opção 1 de Defesa: Instruções Preparadas (Consultas Parametrizadas) ou a Opção 2 de Defesa: Procedimentos Armazenados , não crie sua própria ferramenta, use a API de Segurança Corporativa do OWASP . Do ESAPI OWASP hospedado no Google Code:

Não escreva seus próprios controles de segurança! Reinventar a roda quando se trata de desenvolver controles de segurança para cada aplicativo da Web ou serviço da Web leva ao desperdício de tempo e a enormes falhas de segurança. Os Toolkits da API de Segurança Corporativa do OWASP (ESAPI) ajudam os desenvolvedores de software a se protegerem contra falhas de design e implementação relacionadas à segurança.

Para obter mais detalhes, consulte Impedindo a injeção de SQL no Java e no SQL Prevention Injection Cheat Sheet .

Preste especial atenção à Opção de Defesa 3: Escapando Todas as Entradas Fornecidas pelo Usuário que introduz o projeto OWASP ESAPI ).

(Isso é uma resposta ao comentário do OP sob a pergunta original; eu concordo completamente que o PreparedStatement é a ferramenta para este trabalho, não as expressões regulares.)

Quando você diz \n , você quer dizer a sequência \ + n ou um caractere real de alimentação de linha? Se for \ + n , a tarefa é bem direta:

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

Para corresponder a uma barra invertida na input, você coloca quatro deles na string regex. Para colocar uma barra invertida na saída, você coloca quatro deles na string de substituição. Isto é presumir que você está criando os regexes e substituições na forma de literais Java String. Se você criá-los de outra maneira (por exemplo, lendo-os a partir de um arquivo), você não precisa fazer tudo o que for escaping em dobro.

Se você tiver um caractere de alimentação de linha na input e quiser substituí-lo por uma sequência de escape, poderá fazer uma segunda passagem sobre a input com isto:

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

Ou talvez você queira duas barras invertidas (não estou muito claro sobre isso):

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

PreparedStatements são o caminho a percorrer na maioria, mas não em todos os casos. Às vezes você se encontrará em uma situação em que uma consulta, ou parte dela, deve ser construída e armazenada como uma string para uso posterior. Confira a folha de dicas de prevenção de injeção SQL no site OWASP para obter mais detalhes e APIs em diferentes linguagens de programação.

Usando uma expressão regular para remover o texto que poderia causar uma injeção de SQL soa como a instrução SQL está sendo enviada para o database através de uma Statement vez de um PreparedStatement .

Uma das maneiras mais fáceis de evitar uma injeção SQL é usar um PreparedStatement , que aceita dados para replace em uma instrução SQL usando placeholders, que não depende de concatenações de strings para criar uma instrução SQL para enviar ao database.

Para obter mais informações, o uso de instruções preparadas dos tutoriais Java seria um bom lugar para começar.

Declarações preparadas são a melhor solução, mas se você realmente precisar fazer isso manualmente, você também pode usar a class StringEscapeUtils da biblioteca Apache Commons-Lang. Tem um escapeSql(String) , que você pode usar:

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

Caso você esteja lidando com um sistema legado, ou tenha muitos lugares para mudar para o PreparedStatement em muito pouco tempo – ou seja, se houver um obstáculo para usar as melhores práticas sugeridas por outras respostas, você pode tentar o AntiSQLFilter

Você precisa do seguinte código abaixo. De relance, isso pode parecer com qualquer código antigo que eu inventei. No entanto, o que eu fiz foi olhar o código-fonte para http://grepcode.com/file/repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.31/com/mysql/jdbc/PreparedStatement. java . Então, depois disso, examinei cuidadosamente o código de setString (int parameterIndex, String x) para localizar os caracteres que ele escapa e personalizei isso para minha própria class, para que possa ser usado para os propósitos que você precisa. Afinal, se esta é a lista de personagens que o Oracle escapa, saber isso é realmente reconfortante em termos de segurança. Talvez a Oracle precise de um empurrão para adicionar um método semelhante a este para a próxima versão principal do 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(); } } 

Depois de pesquisar um lote de teste de solução para evitar sqlmap de injeção de sql, no caso de sistema legado que não podem aplicar instruções preparadas em todos os lugares.

O tópico java-security-cross-site-scripting-xss-and-sql-injection foi a solução

Eu tentei solução de @Richard, mas não funcionou no meu caso. eu usei um filtro

O objective desse filtro é agrupar a solicitação em um wrapper próprio codificado, MyHttpRequestWrapper, que transforma:

os parâmetros HTTP com caracteres especiais (<,>, ‘,…) em códigos HTML por meio do método org.springframework.web.util.HtmlUtils.htmlEscape (…). Nota: Há class similar no Apache Commons: org.apache.commons.lang.StringEscapeUtils.escapeHtml (…) os caracteres de injeção SQL (‘, “,…) através da class org.apache.commons.lang.StringEscapeUtils do Apache Commons. escapeSql (…)

  RequestWrappingFilter com.huo.filter.RequestWrappingFilter   RequestWrappingFilter /*  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 escapedParametersValuesMap = new HashMap(); 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 

De: [fonte]

 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; 

}