J'essaie de mettre en place une injection anti-SQL dans Java et je trouve très difficile de travailler avec la fonction de chaîne "replaceAll". Finalement, j'ai besoin d'une fonction qui convertira n'importe quel \
existant en \\
, tout "
en \"
, tout '
en \'
, et tout \n
à \\n
afin que, lorsque la chaîne est évaluée par MySQL, les injections SQL soient bloquées.
J'ai gravé du code avec lequel je travaillais et tous les \\\\\\\\\\\
de la fonction me rendent fou des yeux. Si quelqu'un avait un exemple de cela, je l'apprécierais beaucoup.
PreparedStatements est la solution, car ils rendent l'injection SQL impossible. Voici un exemple simple prenant l'entrée de l'utilisateur comme paramètre:
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 du nom et de l'e-mail, ces caractères seront placés directement dans la base de données. Ils n'affecteront en aucune façon l'instruction INSERT.
Il existe différentes méthodes de définition pour différents types de données - celle que vous utilisez dépend des champs de votre base de données. Par exemple, si vous avez une colonne INTEGER 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.
Le seul moyen d'empêcher l'injection SQL est d'utiliser du SQL paramétré. Il est tout simplement impossible de créer un filtre plus intelligent que les personnes qui piratent SQL pour gagner sa vie.
Utilisez donc des paramètres pour toutes les clauses input, updates et where. Le SQL dynamique est simplement une porte ouverte pour les pirates, et cela inclut le SQL dynamique dans les procédures stockées. Paramétrer, paramétrer, paramétrer.
Si vraiment vous ne pouvez 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 créez pas votre propre outil, utilisez le - API de sécurité d'entreprise OWASP . 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 des pertes de temps et des failles de sécurité énormes. Les boîtes à outils ESAPI (OWASP Enterprise Security API) 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 en Java et Aide-mémoire de prévention de l'injection SQL .
Portez une attention particulière à Option de défense 3: Échapper à toutes les entrées fournies par l'utilisateur qui introduit le projet OWASP ESAPI ).
(Ceci est en réponse au commentaire du PO sous la question initiale; je suis tout à fait d'accord sur le fait que PreparedStatement est l'outil pour ce travail, pas les expressions rationnelles.)
Lorsque vous dites \n
, voulez-vous dire la séquence \
+ n
ou un caractère de saut de ligne? Si c'est \
+ n
, la tâche est assez simple:
s = s.replaceAll("['\"\\\\]", "\\\\$0");
Pour faire correspondre une barre oblique inversée dans l'entrée, vous devez en insérer quatre dans la chaîne regex. Pour insérer une barre oblique inversée dans la sortie, vous devez en insérer quatre dans la chaîne de remplacement. Cela suppose que vous créez les expressions rationnelles et les remplacements sous la forme de littéraux Java. Si vous les créez de toute autre manière (par exemple, en les lisant dans un fichier), vous n'avez pas à faire tout ce double échappement.
Si vous avez un caractère de saut de ligne dans l'entrée et que vous souhaitez le remplacer par une séquence d'échappement, vous pouvez effectuer un second 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");
Les déclarations préparées sont la voie à suivre dans la plupart des cas, mais pas dans tous. 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 le aide-mémoire pour la prévention de l'injection SQL sur le site OWASP pour plus de détails et les API dans différents langages de programmation.
Les instructions préparées sont la meilleure solution, mais si vous avez vraiment besoin de le faire manuellement, vous pouvez également utiliser la classe StringEscapeUtils
de la bibliothèque Apache Commons-Lang. Il a une méthode escapeSql(String)
, que vous pouvez utiliser:
import org.Apache.commons.lang.StringEscapeUtils; … String escapedSQL = StringEscapeUtils.escapeSql(unescapedSQL);
L'utilisation d'une expression régulière pour supprimer du texte, ce qui pourrait provoquer une injection SQL, donne l'impression 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 est d’utiliser un PreparedStatement
, qui accepte la substitution de données dans une instruction SQL utilisant des espaces réservés, qui ne repose pas sur des concaténations de chaînes pour créer une instruction SQL à envoyer à la base de données.
Pour plus d'informations, Utilisation d'instructions préparées à partir de Le Java Didacticiels serait un bon endroit pour commencer.
Vous avez besoin du code suivant ci-dessous. En un coup d'œil, cela peut ressembler à n'importe quel ancien code que j'ai composé. Cependant, ce que j’ai fait, c’est de regarder le code source de 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 examiné le code de setString (int parameterIndex, String x) pour rechercher les caractères qu’il échappe et l’a personnalisé dans ma propre classe afin qu’il puisse être utilisé aux fins souhaitées. Après tout, s’il s’agit de la liste de caractères échappée par Oracle, il est alors rassurant de le faire pour la sécurité. Peut-être qu'Oracle a besoin d'un Nudge pour ajouter une méthode similaire à celle-ci pour la prochaine version majeure 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();
}
}
Si vous avez affaire à un système existant, ou si vous avez trop d’endroits pour passer à PreparedStatement
s en trop peu de temps - c’est-à-dire qu’il existe un obstacle à l’utilisation de la meilleure pratique suggérée par d’autres réponses, vous pouvez essayer - AntiSQLFilter
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;
}
Je pense que escapeSql
de StringEscapeUtils pourrait faire l'affaire. Pourtant, essayer mais cela semble être une option crédible.
Après avoir recherché plusieurs solutions de test pour éviter l’injection de sqlmap, en cas de système hérité qui ne peut pas appliquer les instructions préparées partout.
rubrique Java-sécurité-cross-site-scripting-xss-et-sql-injection ETAIT 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'encapsuler la demande dans un wrapper codé en propre, MyHttpRequestWrapper, qui transforme:
les paramètres HTTP avec des caractères spéciaux (<,>, ‘,…) en codes HTML via la méthode org.springframework.web.util.HtmlUtils.htmlEscape (…). Remarque: Il existe une classe similaire dans Apache Commons: org.Apache.commons.lang.StringEscapeUtils.escapeHtml (…) les caractères d'injection SQL (', “,…) via la classe Apache Commons 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;
}
}