21 de abril de 2011

Protegiendo los formularios de autentificación e ingreso.

Los formularios de autentificación de usuarios son unas de las rutinas más atacadas de las aplicaciones web, debido a que ellos son los que separan a los atacantes de los que podríamos definir como el cofre al final del arcoiris. En los sitios web que utilizan formularios de autentificación basados en consultas SQL, varios tipos simples de "SQL injectión" pueden ser utilizados para eludir la validación del usuario. Por ejemplo, una consulta SQL típica para verificar dicha validación podría ser la siguiente:

SELECT * FROM users WHERE username = 'nombreUsuario' AND password = 'claveUsuario'

En esta consulta se reemplazarían a nivel de aplicación "nombreUsuario" y "claveUsuario" por los contenidos introducidos por el usuario en el formulario de validación y si una validación correcta de los parámetros ingresado no es efectuada, un atacante podría introducir en el primer campo la secuencia de caracteres "admin' --" (las comillas dobles son para efectos de redacción de este artículo, no las tome en cuenta). Lo anterior significaría que al reemplazar los valores la consulta resultante sería:

SELECT * FROM users WHERE username = 'admin' --AND password = 'cualquier cosa' 

La consulta resultante sería realmente:

SELECT * FROM users WHERE username = 'admin'

Todo lo que se encontrara después de los dos guiones pasarían a ser simples partes de un comentario, por tanto la consulta devolvería los datos del usuario "admin" sin siquiera haber verificado la clave del mismo. Como se podrá observar esta es una consulta demasiado simple para necesitar herramientas automatizadas de pen-testing, y su principal error además de la falta de desinfección de parámetros es el uso común de un nombre de usuario como "admin" que de seguro se encuentra en absolutamente todas las listas de las herramientas de intrusión existentes.

Otro método un poco más elaborado se basa en introducir un condicional en el campo de la clave, en este caso introduciremos el código inyectado  "' OR 1=1 --" (nuevamente no tome las comillas dobles en cuenta).

La consulta resultante esta vez sería:
SELECT * FROM users WHERE username = 'admin' AND password = 'cualquierCosa' OR 1=1 --'

La segunda parte de esta consulta preguntará a la maquinaria SQL si el password del usuario es correcto o si uno es igual a uno, y como basta que una de las dos sea cierta la sentencia devolverá nuevamente TRUE por lo que el resultado traerá nuevamente los datos del usuario sin verificar la clave.

Lo que importa realmente entender de los anteriores ejemplos son dos detalles importantes:
  1. Todos lo campos deben ser desinfectados. Existe la absurda creencia de que el único campo vulnerable es el que contiene la clave del usuario, y como se ha podido observar se puede iniciar la inyección de código desde el primer campo de una forma ridículamente simple.
  2. No solo es necesario proteger la clave de los usuarios, el hecho de que el identificador comúnmente conocido como nombre o "login" pueda ser obtenido, es una vulnerabilidad en si misma. Esto sucede principalmente por devolver respuestas inadecuadas a la hora de un ingreso erróneo en el formulario de acceso. 
El atacante utiliza nuestras respuestas de error para obtener usuarios válidos. Basado en nombres compuestos con números, es posible obtener la gran mayoría de los nombres de usuarios de una muestra. Las respuestas que ayudan a validar si estos existen son:
  • "Error de ingreso: la clave proporcionada no es correcta", en este caso si la clave no es correcta entonces debe ser que el usuario si lo es.
  • "Error de ingreso: El nombre de usuario proporcionado no existe!", y en este otro caso se despeja la duda, este usuario en realidad no existe.
Estas son la forma más eficiente para que el atacante intente obtener nombres de usuarios validos. La respuesta correcta debería ser:
  • "Error de ingreso: La combinación de usuario y clave proporcionados no es válida" o algo parecido que no indique a ciencia cierta cual de los dos campos es el incorrecto o si ambos lo son.
Este  punto de la obtención de usuarios válidos no solo refiere a intentos de ataque cuando se trata de inyecciones SQL. Es importante entender que los sistemas de intrusión por fuerza bruta utilizan dos tipos de ataques: Ataques de profundidad y ataques de amplitud.

Los ataques de profundidad son aquellos que se realizan intentando ingresar con un mismo "login" o nombre de usuario y miles de diferentes posibles claves de acceso. Solo cuando se termina de comprobar la lista de claves de acceso se pasa entonces a otro nombre de usuario. Sin embargo los ataques de amplitud son aquellos en los que se utiliza una misma clave o password, para una lista de posibles usuario.

En base a lo anterior, un ataque de amplitud sería mucho más útil si ya conocemos una cantidad determinada de nombres de usuario, debido a que el atacante sabe sin duda alguna que estos no pueden repetirse bajo ningún concepto, sin embargo las claves de acceso si pueden ser iguales para usuarios diferentes, no existe ninguna regla que lo prohiba.

También hay formularios no validan contra una maquinaria SQL sino por ejemplo contra un servicio LDAP o archivos estaticos XML. En estos casos la diferencia no es tan notable, en vez de usar técnicas de "SQL injection" se utilizarán "LDAP injection" o "XPath injection", y aunque la sintaxis de la injección sea diferente los principios de defensa son exactamente los mismos. A saber:
  • Desinfectar los datos recibidos por parte del usuario.
  • Proteger los "logins" o nombres de usuario, mediante respuestas de error que no aseguren nada. Este punto es válido también para los sistemas derecuperación de clave, que suelen ser uno de los primeros agujeros por donde obtener usuarios válidos.
  • Utilizar sistemas de bloqueo de acceso con máximo de intentos. Estos deben ser procedimientos del lado del servidor. El bloqueo mediante "cookies" no es útil cuando los formularios son forjados o el ataque proviene de una herramienta de ingreso por fuerza bruta.

Esperando que estos consejos le sean útiles me despido hasta la próxima...

No hay comentarios.:

Entradas populares