martes, 14 de enero de 2014

Las mejores inyecciones son chorizos y jamones (un cuento de inyección SQL). Parte 1

Bien es verdad que hace casi 4 meses que no escribo en este blog, pero este año me he propuesto escribir un poco más que el pasado. Cosa que no será muy difícil, ya que el año pasado sólo escribí cinco entradas. De momento, he empezado con una serie que me dará para varias entradas.

Bueno, desde hace ya muchos años en miles de sitios se ha estado hablando por activa y por pasiva sobre el problema de la inyección SQL (SQL Injection). Ahora que ya no está de moda, llego yo, como siempre, tardío a las modas, y decido que voy a dedicar también una entrada a este tema. Mi intención no es enseñar nada a nadie, sino aprender yo, y como creo firmemente que la mejor forma de aprender es explicar las cosas a otros, pues voy a dar algunas explicaciones que a mí me serán útiles como si se las explicara a otro, y si le son útiles a alguien más, pues mejor.

Al fin y al cabo prevenirse contra un ataque de inyección SQL es una cuestión básica en la seguridad de nuestras aplicaciones web, ¿no? Pues a ver si nos lo creemos.

1. Qué es SQL Injection

La forma clásica de exponer el problema de la Inyección SQL es más o menos como sigue. Imaginenos un formulario en el que se piden datos al usuario. Para darle al tema el dramatismo que merece toda buena historia, digamos que se pide algo crítico, como... ¡tachán! El nombre de usuario y la contraseña. 

Si un atacante es capaz de saltarse este mecanismo de control podría acceder a nuestra BD super-importantísima-mega-secreta y descubrir los datos de nuestro plan para dominar el mundo. Por lo tanto, hay que protegerse.

1.1 Un contexto de cuento

Pongamos un contexto ficticio: supongamos que la madre de Caperucita (nunca supimos su nombre, ¿Caperumadre?) ha decidido que no quiere enviar a su chiquilla por el bosque camino de la casa de la abuelita con una cesta repleta de alimentos escogidos al azar. En vez de ello, y ya que la abuela es una quejica, ha decidido crear una base de datos con información sobre el contenido de su despensa (esos arándanos son la repera), y ha desarrollado una aplicación web para que la abuelita acceda a ella y haga su pedido. Ya de paso, ha puesto un negocio en Internet y sirve a otras abuelitas de la zona. Al fin y al cabo, Caperucita cobra poco por hacer los repartos, y su vida tampoco corre tanto riesgo: al fin y al cabo, sólo hay un lobo en el bosque, ¿no? 

De esta forma, cuando Caperucita eche el viaje, el contenido de su cestita coincidirá completamente con lo que la abuelita más desea (miel, lentejas, pastelillos de cabello de ángel, y todas esas cosas que les gustan a las abuelas) en vez de lo que la Caperumadre quiere enviarle a su progenitora.

Pero el lobo, escarmentado de intentar comerse a Caperucita y a su abuelita, ha cambiado de objetivo, y solamente pretende robarle la capaza a Caperu (así la llaman los colegas) y ya puesto, le gustaría que el contenido de la cesta fuera de las cosas que le gustan a un lobo: carne de conejo, sobre todo. Para ello, sabe que si puede hackear la base de datos y hacer un pedido en nombre de la abuelita, se asegura de que la cesta va repleta de lo que a él le gusta. Otra cosa es que pueda robarle la capaza a Caperu, pero eso ya es harina de otro costal.

1.2 Al ataqueeeeerrrrr

¿Cómo puede actuar el lobo, potencial atacante de la web de la Caperumadre? Pues empezando a imaginar varias cosas. Por ejemplo, puede imaginar que se accede a la BD mediante ODBC, lo cual es una suposición bastante razonable, ya que en muchos casos se hace así. También puede imaginar que si el programador ha hecho las cosas regular (o sea, mal) puede ser que una vez recuperados los dos campos del formulario (usuario y password), internamente esté construyendo una instrucción del tipo

 1 $usuario = $_POST['usuario']
 2 $pwd = $_POST['password']
 3 $sql = "select * from usuarios where usuario = '$usuario' and password = '$pwd'"

(los nombres de los parámetros POST y el nombre de la tabla y las columnas no son relevantes, de momento)

Y, a continuación, ejecutar la sentencia resultante contra la BD:

 1 $conn = conectar_con_la_BD();
 2 $rs=odbc_exec($conn,$sql); 

y si esa instrucción no produce resultados, impedir el acceso a la aplicación. En realidad, por simplificar el ejemplo, estoy saltándome algunos pasos como codificar la contraseña con alguna función tipo MD5, SHA1 o cualquier otro mecanismo de encriptación, o usar un protocolo de autenticación un poco más complicado. Pero para el ejemplo, vale así.

Entonces, ese atacante malo malísimo podría introducir en el campo password algo así:

 1 ' OR '1' = '1'

(el número 1 que aparece en rojo no se introduciría, es el número de la línea de código).

Pensando que de esa manera, la instrucción SQL que se ejecutaría sería la siguiente:

 1 select * from usuarios where usuario = 'pepe' and password='' OR '1' = '1'

Y esto siempre producirá resultados (siempre y cuando la tabla de usuarios tenga algo, claro).

Ejercicio fácil: ¿por qué podemos afirmar que esto siempre produciría resultados?


Si además el atacante quiere hacer daño borrando una tabla, por ejemplo, podría escribir en el campo password del formulario algo así como:

 1 '; drop table usuarios; --

Con lo que la instrucción a ejecutar sería algo así:

 1 select * from usuarios where usuario = 'pepe' and password = ''; drop table usuarios; --

El doble guión -- es el carácter de comentario, para ignorar el resto de la línea, si es que hubiera cosas más allá del where (por ejemplo, una cláusula order by, u otras condiciones concatenadas con un AND).

Eso implica que EN TEORIA se ejecutarían las dos instrucciones SQL, la primera hace el select y la segunda ejecuta el DROP, cargándose la tabla de usuarios. En el segundo caso, el atacante se supone que sabe (o está imaginando) algo más.

Hasta aquí, la forma clásica de exponer más o menos el problema. Pero basta de teoría. En una próxima entrada haremos algunas pruebas, a ver si esto funciona.

Referencias:
[1] Inyección SQL en la wikipedia. http://es.wikipedia.org/wiki/SQL_injection

2 comentarios:

  1. He leído unos párrafos... cuando los asimile seguiré!! Fijo que lo voy a leer todo, ni lo dudes!! Después haré mi reflexión. ;-)

    ResponderEliminar
  2. Bueno, pero tampoco reflexiones mucho. Más bien, ¡haz! Prueba, imagina nuevas formas de saltarte los controles, prueba variaciones a mis propuestas, y, sobre todo, no te conformes con lo (poco) que yo digo aquí, que apenas es la punta del iceberg en este tema.

    Y muchas gracias por pasarte por aquí.

    ResponderEliminar