Introducción
“SQL Injection” es un tipo de ataque que se ha vuelto muy popular en los últimos años por la utilización cada vez más habitual de sitios web que interactúan con bases de datos SQL. Si a esto le sumamos que la mayoría de los programadores no se capacitan en técnicas y metodologías de seguridad, terminamos con aplicaciones web mal diseñadas o con faltas de controles en la validación de datos, por lo que se vuelven aplicaciones vulnerables.
Al final del artículo veremos algunas contramedidas útiles para evitar este tipo de ataques.
El objetivo de este ataque es insertar o modificar consultas a una base de datos SQL (como MS SQL Server, MySQL, PostgreSQL, Oracle, etc) a través de una consulta al sitio web vulnerable para obtener resultados diferentes a los deseados por el desarrollador de la aplicación. Con esto, por ejemplo, podríamos lograr evitar una validación de usuario y contraseña, u obtener datos confidenciales que se alojan en la base.
Abajo podemos ver un ejemplo de código en PHP, que concatena las variables $login y $pass directamente dentro de la consulta SQL, sin realizar ninguna validación del contenido de las variables.
$entrada = mysql_query(“SELECT usuario FROM usuarios WHERE usuario=’$login’ and clave=’$pass'”)
if(mysql_num_rows($entrada)==’0′)
{ echo ‘Acceso Denegado’; }
else
{ echo ‘Acceso Permitido’; }
El objetivo del código es buscar en la base de datos de usuarios si exista alguna coincidencia para un nombre de usuario y claves iguales a los que se están introduciendo en el formulario web.
Si nosotros logramos que el contenido de la variable $login sea “fabian” y el contenido de la variable $pass sea igual a:
hack’ or ‘1’=1′
Esto simplemente podríamos lograrlo rellenando el formulario con los datos: “fabian” y “hack’ or ‘1’=1′” (sin las comillas dobles) en los campos usuario y contraseña.
La consulta que se realizaría a la base de datos sería la siguiente:
SELECT usuario FROM usuarios WHERE usuario=’fabian’ and clave=’hack’ or ‘1’=’1′;
El resultado de esta consulta va a devolver los registros de todos los usuarios, porque en la segunda parte de la consulta (que va después del ‘or’) estamos pasando una sentencia que siempre va a ser verdadera (‘1’=’1’). Gracias a esto, se van a devolver registros y el programa va a permitir el acceso.
Evidentemente, la posibilidad de los ataques depende tanto del lenguaje en el que está programado el sitio web (generalmente PHP y ASP) y de la base de datos que se está utilizando.
Además, existen muchas otras técnicas para realizar SQL Injection, pero requieren una excelente comprensión, tanto de las bases de datos, como de los lenguajes de programación. Siempre con la ayuda de un poco de suerte, porque debemos adivinar varios comportamientos de la aplicación, para determinar cómo atacarla.
Herramientas de ataque
Existen varias herramientas que podemos utilizar para facilitarnos las tareas de encontrar vulnerabilidades en los sitios web. Como siempre, es mejor que, por lo menos, tengamos buenos conocimientos acerca de los conceptos, antes de utilizar estas herramientas.
Algunas de las más destacadas son:
sqlmap http://sqlmap.sourceforge.net
sqlninja http://sqlninja.sourceforge.net
Contramedidas
Escapar las consultas
Una de las funciones de las que disponemos en PHP para luchar contra este tipo de ataques es “mysql_real_scape_string()”, que toma como parámetro una cadena, y la modifica evitando todos los caracteres especiales, por lo que la vuelve segura para introducirla a la función “mysql_query()”.
Verificar los tipos y longitud de datos
Si esperamos recibir un cierto tipo de datos, no podemos simplemente confiar que lo sea. Existen funciones para verificar tipos de valor, como “is_int()”, “is_long()”, “is_char()”, y otras. También podemos convertir entre tipo, con “settype()”.
También debemos comprobar la longitud de los datos, por ejemplo con “strlen()”
Programar bien!
Si, si, la mejor contramedida para evitar este tipo de ataques es programar bien y con un buen entendimiento de la seguridad informática. Aunque sea, debemos estar al tanto de los conceptos relacionados con la seguridad en aplicaciones web. No es necesario que seamos ‘hackers’ o expertos en seguridad para realizar esto, simplemente es necesario contar con las bases y el interés de desarrollar de forma segura.
Más información:
http://es.wikipedia.org/wiki/Inyecci%C3%B3n_SQL
En algunos de los siguientes artículos volveremos a tratar los temas de SQL Injection y analizaremos otras herramientas.
Aprovecho estas líneas para invitarlos a mi newsletter mensual, en el cual podrán encontrar más información acerca de noticias, herramientas y novedades acerca de la seguridad informática: www.portantier.com/educacion/newsletter
¡Hasta la próxima!
Fabian Portantier
www.portantier.com
twitter.com/#!/portantier
Felicidades pepo, conseguiste lo que querias, tus 5 min o 2 commen de fama, como lo quieras ver, ahora a chingar a su madre!!!
pues no eres el único en el mundo para catalogar los artículos, este articulo es abierto para todos así que si no te gusta por que mejor no vas a otra página o te dedicas a otra cosa y dejas a lo que se interesan en el tema
Como estas Fabian , gracias por tus articulos estan buenos!! Saludos!!
Hola Edwin, muchas gracias por tu comentario y por el aporte de tu caso en C#.Lo de programar en capas es una excelente práctica, aunque no todas las personas lo hacen.
salu2!
Hola Carlos, muchas gracias por tu comentario!
Existen muchos sitios web que no consideran estos temas (aunque hace años se conocen los ataques SQL Injection).
Cosas como las que mencionás, que parecen básicas, muchas veces no suelen tenerse en cuenta.
En algún próximo artículo vamos a hablar en general de las medidas básicas de seguridad, que parecen “simples y básicas”, pero que son muy pero muy importantes.
salu2!
Muchas Gracias Alejandro!
No conozco Java en profundidad, así que también me sirve para ir aprendiendo.
salu2!
Perdón si no te gustan los artículos, decime donde puedo leer los tuyos así comparamos. Vos qué aporte le hacés a la comunidad? Cuántos textos escribiste?
Patetico?
Yo soy Fabian Portantier, y vos te hacés llamar “pepo”, antes de comentar, y ser ofensivo, por lo menos decí quién sos, porque si no no se puede entablar un buen debate. No te parece?
salu2 y muchísimas gracias por tu comentario. Esas con las críticas constructivas que buscamos en nuestros artículos.
Escuela de Hacking? Patetico
Y encima la nota afanada de diversos articulos. De donde sacan estos comentaristas?
Buen informe. Yo estoy en el tema desde el 2005. Y si, todavia hay muchas paginas con estos problemas.
En lo personal, en los campos donde el usuario rellena “Nombre, apellido, email,…. y todos los que se ocurra”, cada vez que presiona el boton “Enviar” (o similar): con codigo bien de programacion chequeo campo por campo. Es MUCHO trabajo, pero con esto evito que el “Usuario” interactue con mi base de datos.
(Perdon si mi explicacion ya era tomada MAS que en cuenta para todo el tema)
Igualmente esto es muy util.
Probando un poco con el código descubrí una forma de evitar
el “inject sql”, con una simple función aplicada a la cadena
que ingresa el usuario; de esta manera solo reconoce
caracteres 0-9 a-z A-Z, el resto los omite.
public static String getEscapeSql(String s) {
StringBuilder str = new StringBuilder();
Pattern p = Pattern.compile(“\\w+”);
Matcher m = p.matcher(s);
if (m.lookingAt()) {
str.append(m.group());
while (m.find())
str.append(m.group());
}
else
str.append(“”);
return str.toString();
}
String sql = “SELECT * FROM DOCS_DESC WHERE NOM LIKE ‘%”
+ getEscapeSql(txtNom.getValue().toString())
+ “%’ LIMIT 100;”;
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql);
Alejandro.
Con respecto a los certificados SSL/TLS, no dependen de los productos que se estén utilizando. Las cosas más importantes a tener en cuenta con las longitudes de claves y los algoritmos de encripción.
Pero es tema para otro artículo, que seguramente va a venir rápido, para mantener la línea de lo que es “seguridad en entornos web”.
salu2!
En este caso el tipo de dato es siempre una cadena -String-
y lo que hace el usuario es introducir como parámetro un
cadena similar a la opción de búsqueda; es decir, por ejemplo
colocar en “nombre” algo como “San” y el resultado sería algo
como {“Sanchez”, “Santiago”, “San Martin”}. Según lo que
propenés en tu respuesta entiendo que la opción sería pensarlo
al reves y solo habilitar, para este ejemplo, en “nombre” los
caracteres a-z A-Z.
Entiendo que si la validación se hace del lado del cliente con
JavaScript, tal vez se pueda crear código malicioso que omita
esta validación, por eso lo dela comprobación del lado del
servidor. En mi caso el JavaScript se genera de forma
automatizada a través de framework Vaadin, y se utiliza, como
decis, para presentar datos y manejar eventos; osea que la
comprobación se efectúa dentro del servlet.
Otra cuestión de las aplicaciones web, es la posibilidad de ser
interceptado el tráfico -mediante snifers y peor en redes wifi-
para esto la medida de seguridad aconsejada es utilizar
protocolos seguros como http sobre ssl o tls -https-. En este
caso conozco 2 formas de utilizar los certificados de
autenticación los autofirmados y los firmados por autoridad de
verificación. Para un sistema en una red interna en una Pyme
supongo que no es necesario un nivel de seguridad elevado, por
lo que con los certificados autofirmados quizá sea suficiente.
La pregunta es: ¿Como generar los certificados autofirmados de
manera correcta para evitar que sean vulnerados? Si hablamos que
los sistemas se desarrollan en entorno web las herramientas que
utilizamos son los servidores tomcat, jetty y apache. Supongo
que daría el tema para un artículo, ya que en internet es
difícil encontrar una explicación de forma sencilla, didáctica
y en español
Saludos;
Alejandro.
Guau, Alejandro, excelente pregunta!
Para ser bien específicos, habría que analizar más a fondo la aplicación, y el tipo de base de datos que se está utilizando.
Como consejos generales, sumado al clásico “Escape de caracteres”, podría decirte lo siguiente:
Denegar caracteres: Dependiendo el caso, seguramente existe sólo un pequeño número de caracteres válidos, como por ejemplo:
dirección de mail: a-Z, 0-9, @
edad: 0-9
nombre: a-Z
apellido: a-Z
direccion: a-Z, 0-9
sexo: M,F
etc, etc, etc
Como podemos observar, la mayoría de los campos no necesita que se puedan ingresar símbolos. La única excepción más normal sería el mail y el ‘@’, que también se podría evitar (haciendo dos campos en el formulario), pero sería demasiado complicado para el usuario.
Comprobar tipos de datos: Otra cuestión interesante, es la de validar si el tipo de datos que se está ingresando es correcto, eso se puede hacer fácilmente en todos los lenguajes. En el caso de que no sea un tipo de datos correcto, podemos directamente no ejecutar la consulta SQL. Con tipos de datos me refiero a: “int”, “long”, “boolean”, etc.
Validar siempre del lado del servidor: JavaScript siempre debe considerarse sólo para hacerle la vida más fácil al usuario, mostrar mensajes de ayuda, validar datos y otras cuestiones. Pero NUNCA como medida de seguridad, porque JavaScript se puede deshabilitar fácilmente. SIEMPRE debemos realizar las validaciones del lado del servidor. Esto no quiere decir que esté mal validar con JavaScript, pero debemos verlo como una ayuda más al usuario y no como algo para incrementar la seguridad de nuestra aplicación.
Por otro lado, recordemos que, de una u otra forma, la aplicación podría ser vulnerable (no sólo existen ataques de SQL Injection, si no también otros, como XSS). Así que ahí habrá que verificar qué tan importante y crítica es esa aplicación. No es lo mismo un HomeBanking que un foro.
Y, por último, una medida adicional podría ser implementar ModSecurity (en el caso de que el servidor web sea Apache). Aunque en tu caso puntual no creo q sea válido, porque estás utilizando una aplicación Java.
Espero haberte ayudado.
saludos!
Fabian, tengo un caso específico de “sql injection” en lenguaje Java,
utilizando Statement’s, por ejemplo:
String sql = “SELECT * FROM DOCS_DESC WHERE NOM LIKE ‘%”
+ txtNom.getValue().toString()
+ “%’ LIMIT 100;”;
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql);
En dicho ejemplo es posible introducir caracteres de escape
para ejecutar codigo SQL dentro de la consulta; según muchos
foros de programación, como StackOverflow, lo correcto es
siempre utilizar PreparedStatement’s en lugar de Statement’s,
por ejemplo:
String sql = “SELECT * FROM DOCS_DESC WHERE NOM LIKE ?”
+ ” LIMIT 100;”;
PreparedStatement ps = con.prepareStatement(sql);
ps.setString(1, txtNom.getValue().toString());
ResultSet rs = ps.executeQuery();
Ahora, utilizar PreparedStatement’s tiene el inconveniente al
momento de utilizar el predicado LIKE, ya que no puedo
generar consultas utilizando los comodínes ‘%’, para hacer
lecturas que tengan en cuenta un valor de campo aproximado
-por ejemplo: para encontrar los registros que tengan la
cadena “venta” dentro del campo -; la opción
para evitar la introducción de caracteres de escape es
realizar algo parecido al siguiente ejemplo:
String sql = “SELECT * FROM DOCS_DESC WHERE NOM LIKE ‘%”
+ txtNom.getValue().toString()
.replace(“\'”, “\\\'”)
.replace(“\””, “\\\””)
+ “%’ LIMIT 100;”;
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql);
Pero según muchos foros de programación sigue siendo inseguro
porque hay que tener en cuenta todas las posibilidades de
caracteres nocivos que pueden ser introducidos que, según la
base de datos que se utilice, muchas veces varian.
La pregunta es: ¿Existe un forma segura de ejecutar el predicado
LIKE utilizando el comodín ‘%’?
Y, si es posible que con un ejemplo puedas desarrollar la forma
correcta de escribir el codígo en Java.
Gracias;
Alejandro.
Yo programo con c# y como base de datos uso sql server 2005. Normalmente programo en 3 capas, asi que en la capa de datos hago las consultas con parametros, esto evita la inyeccion de codigo en las consultas. No se que pasa con otros motores de bases de datos. En todo caso, es un muy buen articulo porque se de muchos programadores de .net que no toman ninguna medida de seguridad.
Saludos.
Sí, PHP y ASP son los 2 lenguajes de programación más utilizados para desarrollar aplicaciones web.
salu2!
Existen muchísimos, sobre todo en el caso de las aplicaciones web desarrolladas “in company”.
En el caso de utilizar sistemas del tipo CMS (como Joomla, Drupal, etc) estamos trabajando sobre código mucho más auditado y probado, que suele estar más asegurado contra este tipo de ataques. Pero igualmente se puede llegar a encontrar algo.
Actualmente, muchas aplicaciones web son vulnerables a SQL Injection.
salu2!
En ese caso habría que ver cómo está armada la SP y si se le pasan parámetros o no. Además, también habría que ver qué hace el código (PHP, ASP, etc) con las variables que rellena el usuario en el formulario.
Podría ser un poco más difícil, pero definitivamente posible.
salu2!
En la gran mayoria de los casos las páginas web están en PHP y ASP, algunas otras en Flash pero no son mayoria
“generalmente PHP y ASP”?
El artículo es muy preciso e interesante. Sin embargo por otro lado me cuesta creer que haya sitios todavía vulnerables a este tipo de ataques. Pensaba que era algo “obsoleto”.
Ohhh.. Little bobby tables ! 😀
muy bueno!
En caso de utilizar Stores Procedures, se podría evitar este tipo de ataques?