martes, 3 de noviembre de 2009

Que es SQL injection?

Bueno acerca de este tema en particular, creo que hay mucha información, puesto que es algo que se ve mucho, mas si eres un desarrollador sin experiencia, pero veamos que nos dice Wikipedia acerca de esto "Inyección SQL es una vulnerabilidad informática en el nivel de la validación de las entradas a la base de datos de una aplicación. El origen es el filtrado incorrecto de las variables utilizadas en las partes del programa con código SQL. Es, de hecho, un error de una clase más general de vulnerabilidades que puede ocurrir en cualquier lenguaje de programación o de script que esté incrustado dentro de otro.".


Bueno, entonces una persona diestra en SQL Injection es un peligro para mi web site... claro que si! El SQL Injection no solo puede hacer maldades, sino también eliminar información importante o incluso la tabla entera.

Como puede hacerse esto? bueno acá hay un ejemplo muy bueno de que es esto.

como podemos obtener acceso a un sitio sin validar, con una simple sintaxis de SQL... por donde lo mires, apuesto a que la gran mayoría de ustedes queridos lectores no había tomado en cuenta que algo así se podía hacer.
Usuario: ' OR 1 = 1; /*
Password: */--
Interesante no! A veces podemos confiarnos de muchas cosas, porque simplemente... a mi no me va a pasar, pero siempre existen personas con intenciones para nada honorables, las cuales estan esperando el menor descuido de un webmaster, para cometer sus fechorías, pero no solo ese tipo hay. Imaginemos este otro ejemplo.

Tenemos en nuestro sitio una consulta de este tipo:
consulta := "SELECT * FROM usuarios 
WHERE nombre = '" + nombreUsuario + "';"
Si el usuario escribe su nombre, digamos "Juan de los palotes", nada anormal sucedería, la aplicación generaría una sentencia SQL similar a la siguiente, que es perfectamente correcta, en donde se seleccionaría al usuario "Juan de los palotes":
SELECT * FROM usuarios WHERE nombre = 'Juan de los palotes';
Pero si un usuario malintencionado escribe como nombre de usuario:

"Juan de los palotes'; DROP TABLE usuarios; SELECT * FROM datos WHERE '-' = '-", se generaría la siguiente consulta SQL, (el color verde es lo que pretende el programador, el azul es el dato, y el rojo, el código SQL inyectado):
SELECT * FROM usuarios WHERE nombre = 'Juan de los palotes';
DROP TABLE usuarios;
SELECT * FROM datos WHERE '-' = '-';
La base de datos ejecutaría la consulta en orden, seleccionaría el usuario 'Juan de los palotes', borraría la tabla 'usuarios' y seleccionaría datos que quizá no están disponibles para los usuarios web comunes. En resumen, cualquier dato de la base de datos está disponible para ser leído o modificado por un usuario malintencionado.

Ahi hay otro ejemplo de lo peligroso que puede ser SQL Injection:
Queremos sacar el password de una tabla.. pero.. ¿como?

Hay una instrucción en SQL llamada UNION, que sirve para obtener información de 2 tablas, o..
Bueno.. UNION necesita algunos requisitos.
  • Necesitas meter la misma cantidad de valores que tiene la tabla.
  • Tener un informe de los errores que provocaremos en la instrucción

Bien ahora empecemos, La instrucción a modificar es la siguiente:
SELECT password FROM usuarios WHERE user = '$us'
como ejemplo, si colocamos de usuario: zanahoria
la instrucción que llega seria:
SELECT password FROM usuarios WHERE user = 'zanahoria'
Otra vez necesitamos creatividad regresemos al UNION.
UNION necesita que el numero de columnas sea igual, sino sacara un error y exactamente, lo que necesitamos es un error, que nos diga cuando estamos mal, para saber cuando estamos bien, el UNION se usa de esta forma:
Usuario: ' AND 0 UNION SELECT 1 AND 'l'='
SELECT password FROM usuarios WHERE user = '' AND 0 UNION SELECT 1 AND 'l'=''
para lo cual SQL nos respondera:
The used SELECT statements have a different number of columns: SELECT password FROM usuarios WHERE user = '' AND 0 UNION SELECT 1 AND 'l'=''
Continuando por el mismo camino, podemos hacer que nos regrese un error, al tratar de UNIR un campo de tipo INT (osea que guarda numeros) a un CHAR (guarda letras)

Quedaria algo asi:
' UNION SELECT MIN(Password),2,3,4,5 FROM usuarios WHERE user = 'zanahoria
la sentencia seria esta:
SELECT password FROM usuarios WHERE user = '' UNION SELECT MIN(Password),2,3,4,5 FROM usuarios WHERE user = 'zanahoria'
que nos da como error:
Syntax error converting the varchar value 'naranja' to a column of data type int.
donde el password es naranja

Ven que peligroso es esto, pero por dicha hay muchas estrategias que podemos utilizar para evitar este tipo de cosas, como por ejemplo:
$query_result = mysql_query 
   ( 
         "SELECT * FROM usuarios WHERE nombre = \"" 
     . 
         mysql_real_escape_string($nombre_usuario) 
     . 
         "\"" 
    );
Este codigo en general lo que hace es a la variable nombre usuario, la hace pasar por un filtro, el cual es claramente una funcion PHP, la cual hace una llamada a las librerias de MySQL, las cuales validan las empresion eliminando "backslashes", y algunos caracteres como " \x00, \n, \r, \, ', " y \x1a".
Esto con el fin de frustrar una posible SQL Injection.

O tambien podemos utilizar un codigo de este tipo:
if (!function_exists("GetSQLValueString")) {
function GetSQLValueString($theValue, $theType, $theDefinedValue = "",        $theNotDefinedValue = "")
{
  $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue;

  $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue);

  switch ($theType) {
    case "text":
      $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
      break;   
    case "long":
    case "int":
      $theValue = ($theValue != "") ? intval($theValue) : "NULL";
      break;
    case "double":
      $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL";
      break;
    case "date":
      $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
      break;
    case "defined":
      $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue;
      break;
  }
  return $theValue;
}
}
 Esta funcion va a validar las expreciones que se le envien como parametros, ademas de pasar un filtro el 
$theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue;
//Pregunta si las magic_quotes estan habilitadas, si es asi, entonces utilizar stripslashes para eliminarlas



  $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue); //En caso de que exista la funcion "mysql_real_escape_string", utiliza esa, pero si no utiliza "mysql_escape_string", dependiendo de la version de PHP.
 luego de esto el valor sera clasificado, para su analisis, y por ultimo 
 $LoginRS__query=sprintf("SELECT user, pwrd FROM usuarios WHERE user='%s' AND pwrd='%s'",
    GetSQLValueString($loginUsername, "-1"), GetSQLValueString($password, "password"));
    //Tratara de validar el texto introducido, en la funcion anterior
    $LoginRS = mysql_query($LoginRS__query, $sql_conection) or die(mysql_error());
//Al final ejecutara el query a como venga, luego de los filtros.
Bueno creo que esto es todo por hoy, espero tener mas temas dentro de poco, saludos.

No hay comentarios:

Publicar un comentario