Java-chaîne d'échappement pour empêcher l'injection SQL


J'essaie de mettre en place une injection anti sql en java et je trouve très difficile de travailler avec la fonction de chaîne "replaceAll". En fin de compte, j'ai besoin d'une fonction qui convertira tout \ existant en \\, tout " en \", tout ' en \' et tout \n en \\n de sorte que lorsque la chaîne est évaluée par MySQL, les injections SQL seront bloquées.

J'ai récupéré du code avec lequel je travaillais et tous les \\\\\\\\\\\ de la fonction rendent mes yeux fous. Si quelqu'un il se trouve avoir un exemple de cela, je l'apprécierais grandement.

Author: Tim Pietzcker, 2009-11-28

11 answers

PreparedStatements sont la voie à suivre, car ils rendent l'injection SQL impossible. Voici un exemple simple prenant l'entrée de l'utilisateur comme paramètres:

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

Quels que soient les caractères dans le nom et l'e-mail, ces caractères seront placés directement dans la base de données. Ils n'affecteront en rien l'instruction INSERT.

Il existe différentes méthodes d'ensemble pour différents types de données-celle que vous utilisez dépend de vos champs de base de données. Par exemple, si vous avez un Colonne ENTIÈRE dans la base de données, vous devez utiliser une méthode setInt. La documentation PreparedStatement répertorie toutes les différentes méthodes disponibles pour définir et obtenir des données.

 228
Author: Kaleb Brasee, 2011-04-23 14:22:33

La seule façon d'empêcher l'injection SQL est avec SQL paramétré. Il n'est tout simplement pas possible de construire un filtre plus intelligent que les personnes qui piratent SQL pour gagner leur vie.

Utilisez donc des paramètres pour toutes les clauses input, updates et where. Dynamic SQL est simplement une porte ouverte pour les pirates, et cela inclut dynamic SQL dans les procédures stockées. Paramétrer, paramétrer, paramétrer.

 45
Author: Cylon Cat, 2009-11-28 16:14:16

Si vous ne pouvez vraiment pas utiliser Option de défense 1: Instructions préparées (Requêtes paramétrées)ou Option de défense 2: Procédures stockées, ne construisez pas votre propre outil, utilisez l'API OWASP Enterprise Security API. Depuis le OWASP ESAPI hébergé sur Google Code:

N'écrivez pas vos propres contrôles de sécurité! Réinventer la roue lorsqu'il s'agit de développer des contrôles de sécurité pour chaque application Web ou service Web entraîne une perte de temps et une sécurité massive trou. Les boîtes à outils OWASP Enterprise Security API (ESAPI) aident les développeurs de logiciels à se prémunir contre les failles de conception et de mise en œuvre liées à la sécurité.

Pour plus de détails, voir Prévention de l'injection SQL dans Javaet Feuille de triche de prévention de l'injection SQL.

Portez une attention particulière àOption de défense 3: Échappement de toutes les entrées fournies par l'utilisateur qui introduit le projetOWASP ESAPI ).

 33
Author: Pascal Thivent, 2009-11-28 16:40:40

(Ceci est en réponse au commentaire de l'OP sous la question originale; Je suis entièrement d'accord que PreparedStatement est l'outil pour ce travail, pas regex.)

Quand vous dites \n, voulez-vous dire la séquence \+n ou un personnage de linefeed réel? Si c'est \+n, la tâche est assez simple:

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

Pour faire correspondre une barre oblique inverse dans l'entrée, vous en mettez quatre dans la chaîne regex. Pour mettre une barre oblique inverse dans la sortie, vous en mettez quatre dans la chaîne de remplacement. Cela suppose que vous créez les expressions régulières et les remplacements sous la forme de littéraux de chaîne Java. Si vous les créez d'une autre manière (par exemple, en les lisant à partir d'un fichier), vous n'avez pas à faire tout ce double échappement.

Si vous avez un caractère linefeed dans l'entrée et que vous souhaitez le remplacer par une séquence d'échappement, vous pouvez faire un deuxième passage sur l'entrée avec ceci:

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

Ou peut-être que vous voulez deux barres obliques inverses (je ne suis pas trop clair à ce sujet):

s = s.replaceAll("\n", "\\\\\\\\n");
 17
Author: Alan Moore, 2013-08-10 00:35:39

PreparedStatements sont la voie à suivre dans la plupart des cas, mais pas dans tous les cas. Parfois, vous vous retrouverez dans une situation où une requête, ou une partie de celle-ci, doit être construite et stockée sous forme de chaîne pour une utilisation ultérieure. Consultez la feuille de triche SQL Injection Prevention sur le site OWASP pour plus de détails et d'API dans différents langages de programmation.

 13
Author: jonnieZG, 2011-01-11 15:56:59

En utilisant une expression régulière pour supprimer du texte qui pourrait provoquer une injection SQL, on dirait que l'instruction SQL est envoyée à la base de données via un Statement plutôt qu'un PreparedStatement.

L'un des moyens les plus simples d'empêcher une injection SQL en premier lieu est d'utiliser un PreparedStatement, qui accepte les données à substituer dans une instruction SQL à l'aide d'espaces réservés, qui ne repose pas sur les concaténations de chaînes pour créer une instruction SQL à envoyer à la base de données.

Pour plus informations, En utilisant des instructions préparées de Les tutoriels Java seraient un bon point de départ.

 9
Author: coobird, 2009-11-28 16:14:30

Préparées sont la meilleure solution, mais si vous avez vraiment besoin de le faire manuellement vous pouvez également utiliser la StringEscapeUtils classe de Apache Commons-Lang de la bibliothèque. Il a une escapeSql(String) méthode, vous pouvez utiliser:

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

 8
Author: ToBe_HH, 2017-05-13 21:23:03

Si vous avez affaire à un système hérité, ou si vous avez trop d'endroits pour passer à PreparedStatement s en trop peu de temps-c'est-à-dire s'il y a un obstacle à l'utilisation des meilleures pratiques suggérées par d'autres réponses, vous pouvez essayerAntiSQLFilter

 6
Author: Bozho, 2009-11-28 16:45:08

Vous avez besoin du code suivant ci-dessous. En un coup d'œil, cela peut ressembler à n'importe quel ancien code que j'ai inventé. Cependant, ce que j'ai fait, c'est regarder le code source pour http://grepcode.com/file/repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.31/com/mysql/jdbc/PreparedStatement.java. Puis après cela, j'ai soigneusement regardé à travers le code de setString (int parameterIndex, String x) pour trouver les caractères qu'il échappe et personnalisé ceci à ma propre classe afin qu'il puisse être utilisé à des fins que vous avez besoin. Après tout, si c'est la liste des personnages qu'Oracle échappe, alors le savoir est vraiment réconfortant sur le plan de la sécurité. Oracle a peut-être besoin d'un coup de pouce pour ajouter une méthode similaire à celle-ci pour la prochaine version majeure de 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
Author: Richard, 2016-01-07 08:12:58

Après avoir cherché un tas de solutions de test pour empêcher sqlmap de l'injection sql, dans le cas d'un système hérité qui ne peut pas appliquer les statments préparés partout.

Je ne sais pas si je suis capable de le faire, mais je ne sais pas si je suis capable de le faire.]} ÉTAIT LA SOLUTION

J'ai essayé la solution de @Richard mais cela n'a pas fonctionné dans mon cas. j'ai utilisé un filtre

Le but de ce filtre est d'envelopper la requête dans un code propre wrapper MyHttpRequestWrapper qui transforme:

Les paramètres HTTP avec des caractères spéciaux ( ,‘,,) en HTML codes via le org.springframework.Web.util.HtmlUtils.htmlEscape(…) méthode. Remarque: Il existe une classe similaire dans Apache Commons : org.Apache.commun.lang.StringEscapeUtils.Echappehtml (the) le SQL caractères d'injection ( ' ,", via) via la classe Apache Commons org.Apache.commun.lang.StringEscapeUtils.Echappesql (...)

<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
Author: shareef, 2016-09-11 20:28:46

De:[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;

}

 0
Author: Billcountry Mwaniki, 2017-05-17 12:48:21