prevenir - Java-cadena de escape para evitar la inyección de SQL
evitar inyeccion sql php (11)
(Esto es en respuesta al comentario de la OP en la pregunta original; estoy completamente de acuerdo en que PreparedStatement es la herramienta para este trabajo, no en expresiones regulares).
Cuando dices /n
, ¿te refieres a la secuencia /
+ n
o a un personaje real de salto de línea? Si es /
+ n
, la tarea es bastante sencilla:
s = s.replaceAll("[''/"////]", "////$0");
Para hacer coincidir una barra invertida en la entrada, coloque cuatro de ellos en la cadena de expresiones regulares. Para poner una barra invertida en la salida, coloque cuatro de ellos en la cadena de reemplazo. Esto supone que está creando expresiones regulares y reemplazos en forma de literales de Java String. Si los crea de otra manera (por ejemplo, leyéndolos de un archivo), no tiene que hacer todo ese doble escape.
Si tiene un carácter de salto de línea en la entrada y desea reemplazarlo con una secuencia de escape, puede hacer una segunda pasada sobre la entrada con esto:
s = s.replaceAll("/n", "////n");
O tal vez quieres dos barras invertidas (no lo tengo muy claro):
s = s.replaceAll("/n", "////////n");
Intento poner una inyección anti sql en java y me resulta muy difícil trabajar con la función de cadena "replaceAll". En definitiva, necesito una función que convierta cualquier /
a //
existente, cualquier "
a /"
, cualquier ''
a /'
, y cualquier /n
a //n
para que cuando la cadena sea evaluada por MySQL se bloqueen las inyecciones de SQL.
He activado un código con el que estaba trabajando y todo el ///////////
en la función me está volviendo loco. Si alguien tiene un ejemplo de esto, lo agradecería mucho.
De: [Fuente]
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;
}
Después de buscar una gran cantidad de soluciones para evitar sqlmap de la inyección SQL, en el caso de un sistema heredado que no puede aplicar declaraciones preparadas en todas partes.
El tema java-security-cross-site-scripting-xss-and-sql-injection FUE LA SOLUCIÓN
Probé la solución de @Richard, pero no funcionó en mi caso. utilicé un filtro
El objetivo de este filtro es envolver la solicitud en un contenedor propio codificado MyHttpRequestWrapper que transforma:
los parámetros HTTP con caracteres especiales (<,>, '', ...) en códigos HTML a través del método org.springframework.web.util.HtmlUtils.htmlEscape (...). Nota: Hay classe similar en Apache Commons: org.apache.commons.lang.StringEscapeUtils.escapeHtml (...) los caracteres de inyección SQL ('', ", ...) a través de la clase org de 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;
}
}
El uso de una expresión regular para eliminar texto que podría causar una inyección SQL suena como que la declaración SQL se envía a la base de datos a través de un Statement
lugar de un PreparedStatement
.
Una de las formas más sencillas de evitar una inyección SQL es usar un PreparedStatement
, que acepta datos para sustituirlos en una declaración SQL utilizando marcadores de posición, que no dependen de concatenaciones de cadenas para crear una declaración SQL para enviar a la base de datos.
Para obtener más información, usar declaraciones preparadas de The Java Tutorials sería un buen lugar para comenzar.
En caso de que esté tratando con un sistema heredado, o tenga demasiados lugares para cambiar a PreparedStatement
en muy poco tiempo, es decir, si hay un obstáculo para utilizar las mejores prácticas sugeridas por otras respuestas, puede probar AntiSQLFilter
La única forma de evitar la inyección de SQL es con SQL parametrizado. Simplemente no es posible construir un filtro que sea más inteligente que las personas que piratean SQL para ganarse la vida.
Así que use parámetros para todas las entradas, actualizaciones y cláusulas where. Dynamic SQL es simplemente una puerta abierta para los piratas informáticos, y eso incluye SQL dinámico en procedimientos almacenados. Parametrizar, parametrizar, parametrizar.
Las declaraciones preparadas son el camino a seguir, ya que hacen imposible la inyección de SQL. Aquí hay un ejemplo simple tomando la entrada del usuario como los 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
}
}
}
No importa qué caracteres tengan el nombre y el correo electrónico, esos caracteres se colocarán directamente en la base de datos. No afectarán la instrucción INSERT de ninguna manera.
Existen diferentes métodos de conjunto para diferentes tipos de datos; el que use dependerá de los campos de su base de datos. Por ejemplo, si tiene una columna INTEGER en la base de datos, debe usar un método setInt
. La documentación de PreparedStatement enumera todos los diferentes métodos disponibles para configurar y obtener datos.
Los enunciados preparados son la mejor solución, pero si realmente necesita hacerlo manualmente, también podría usar la clase StringEscapeUtils
de la biblioteca Apache Commons-Lang. Tiene un escapeSql(String)
, que puede usar:
import org.apache.commons.lang.StringEscapeUtils; … String escapedSQL = StringEscapeUtils.escapeSql(unescapedSQL);
Los estados preparados son el camino a seguir en la mayoría, pero no en todos los casos. A veces se encontrará en una situación en la que una consulta, o una parte de ella, debe ser construida y almacenada como una cadena para su uso posterior. Consulte la Hoja de referencia de prevención de inyección de SQL en el sitio OWASP para obtener más detalles y API en diferentes lenguajes de programación.
Necesitas el siguiente código a continuación. A simple vista, puede parecerse a cualquier código anterior que inventé. Sin embargo, lo que hice fue mirar el código fuente de http://grepcode.com/file/repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.31/com/mysql/jdbc/PreparedStatement.java . Luego, después de eso, analicé cuidadosamente el código de setString (int parameterIndex, String x) para encontrar los caracteres que escapa y personalizarlos en mi propia clase para que puedan usarse para los fines que necesite. Después de todo, si esta es la lista de personajes a los que se escapa Oracle, saber esto es realmente reconfortante para la seguridad. Tal vez Oracle necesite un empujón para agregar un método similar a este para la próxima versión principal 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();
}
}
Si realmente no puede usar la opción de defensa 1: declaraciones preparadas (consultas parametrizadas) o la opción de defensa 2: procedimientos almacenados , no cree su propia herramienta, use la API de seguridad empresarial de OWASP . Desde OWASP ESAPI alojado en Google Code:
¡No escriba sus propios controles de seguridad! Reinventar la rueda cuando se trata de desarrollar controles de seguridad para cada aplicación web o servicio web lleva a perder tiempo y agujeros de seguridad masivos. Los Toolkits de la API de seguridad empresarial (ESAPI) de OWASP ayudan a los desarrolladores de software a protegerse contra el diseño relacionado con la seguridad y los defectos de implementación.
Para obtener más información, consulte Cómo evitar la inyección de SQL en Java y en la Hoja de referencia de prevención de inyección SQL .
Preste especial atención a la Opción de Defensa 3: Escapar toda la entrada suministrada por el usuario que introduce el proyecto OWASP ESAPI ).