|
Hace unos meses *descubrí* un grave problema de seguridad que potencialmente podía afectar a muchos sitios web segurizados, con validación a través de usuario y clave. Me tomé este tiempo para definir el problema y chequear a cuántos sitios web afecta. Los resultados son sorprendentes. ** Digo descubrí porque fue realmente así. Esto no quiere decir que nadie en el mundo se haya dado cuenta de ésto antes (aunque creo que no deben ser muchos debido a la magnitud de sitios en todo el mundo con este problema).
El problema afecta a sitios web que utilizan Bases de Datos para contener el listado de pares usuario/clave, páginas ASP para accederlas, y cuyas rutinas de validación de usuario utilizan consultas SQL escritas de una determinada manera.
Muchos desarrolladores web validan el acceso de usuarios a través del siguiente método, implementado además por el sistema de autentificación de usuarios (Server Behavior) de Macromedia Dreamweaver Ultradev 4 (última versión a la fecha): * Se pide al usuario que ingrese el par usuario/clave en campos TEXT y PASSWORD, respectivamente, de un formulario en una página HTML convencional. * Al hacer click sobre el botón SUBMIT del formulario, estos datos son pasados ocultamente a una página ASP en el encabezado HTTP, ya que la etiqueta FORM generalmente tiene (y debería tenerlo siempre en estos casos) el atributo METHOD="POST". * La página ASP toma los datos de usuario y clave. * Se realiza una consulta SQL contra la base de datos para traer el registro que corresponde al usuario y clave ingresados. * Si la consulta devuelve un registro, entonces los datos son correctos, el usuario es reconocido por el sistema, y se crea una variable de sesión que lo habilita de aquí en adelante a ingresar a las partes segurizadas del sitio a las cuales tiene acceso. Si la cantidad de registros devueltos es nula es porque no se ha encontrado ningún par usuario/clave coincidente en la tabla de usuarios. Si es así, se envía a la persona a otra página donde se le indica que hay un error en los datos ingresados.
La página ASP que recibe los datos de usuario y clave, los recupera con las siguientes instrucciones: str_usuario = Request.Form("usuario") y utiliza una consulta SQL contra la base de datos para devolver el registro correspondiente al usuario. La cadena SQL es como sigue (en una sola línea): cad_sql = "SELECT
* FROM tabla_usuarios Como vemos, se van concatenando las instrucciones SQL con la información proveniente del formulario donde el usuario ingresó sus datos. Luego se ejecuta la consulta y se verifica si devuelve un registro o no. En función de ello se habilita o no al usuario.
Qué pasa si en el campo USUARIO del formulario web de validación se introduce el siguiente texto: ' OR col_usuario LIKE '%% y en el campo CLAVE lo siguiente (aunque se vea como asteriscos), ' OR col_clave LIKE '%% Cuando se concatenan los datos en la consulta SQL, la cadena definitiva quedará como sigue: SELECT * FROM tabla_usuarios
Así, se consigue ser validado por el sistema sin siquiera saber como cuál usuario se ha ingresado. Notar que este problema no es tal en otros lenguajes que no permiten la expansión de variables. En PHP, por ejemplo, una comilla simple pasada a través de un método GET o POST es tomada como '\. Es decir, la comilla seguida de una barra invertida. Esto no se puede cambiar, salvo que el administrador del servidor modifique la configuración por defecto de esta propiedad en uno de los archivos ini del intérprete PHP. Ahora, muchos se estarán preguntando cómo sé los nombres de los campos (columnas) de la tabla. Generalmente, los programadores/desarrolladores colocan en el formulario de ingreso de usuario/clave los mismos nombres de las columnas de la tabla en las etiquetas INPUT. Usuario: <input type="text"
name="usuario"> Así, con sólo observar el código de la página HTML de ingreso de datos, posiblemente se obtenga la información necesaria. Sino, se puede inducir un error en la consulta SQL, por ejemplo de la siguiente manera, usuario --> ' OR col_usuario LIKE '%%' Simplemente se ha agregado una comilla al final del nombre de usuario comentado anteriormente. Cuando se concatenen las cadenas de texto, quedará una comilla sin cerrar, lo cual, cuando se ejecute la consulta contra la base de datos, producirá un error en la API de acceso a datos (OLE DB) que saldrá en la pantalla, probablemente junto a la cadena completa de la consulta. De allí simplemente se leen los nombres de los campos que el desarrollador ha asignado. Pero la cosa es más sencilla aún. No se necesitan conocer los nombres de los campos usuario y clave de la tabla. CON SOLO CONOCER EL NOMBRE DE UN CAMPO CUALQUIERA DE LA TABLA, IGUAL SE PODRA ACCEDER COMO USUARIO VALIDADO. Veamos, supongamos que la tabla de usuarios contiene otro campo, además de usuario y clave, llamado NOMBRE. Entonces, si en el formulario HTML de validación se ingresa lo siguiente en los campos usuario y clave usuario --> ' OR NOMBRE LIKE '%% la cadena de consulta concatenada quedará SELECT * FROM tabla_usuarios La cual, si la analizamos, nos devolverá nuevamente todos los registros de la tabla y la validación es positiva.
Con los códigos utilizados anteriormente no se sabe de antemano como cuál usuario se va a ingresar. Ésto dependerá del primer registro obtenido en el Recordset que trae la consulta. Para ser más específico, se puede modificar la cadena SQL. Por ejemplo, para ingresar como una persona cuyo usuario contenga la cadena ana, se debe colocar: usuario --> ' OR NOMBRE LIKE '%ana% NO OLVIDEMOS QUE ESTOS DATOS SE INGRESAN EN LOS CAMPOS USUARIO Y CLAVE DE LA PAGINA DE LOGIN, A LA QUE TODO EL MUNDO TIENE ACCESO. Es más, si se conoce el nombre de un usuario determinado, se puede ingresar al sistema validados como tal. Por ejemplo, supongamos un usuario llamado JOSE; para ingresar como él, se debe introducir lo siguiente usuario --> JOSE La cadena concatenada quedará de la siguiente manera: SELECT * FROM tabla_usuarios Esta consulta devolverá sólo el registro correspondiente al usuario JOSE. Por lo tanto, el sistema nos validará como el mismo.
La solución a este planteo debe resultar trivial para cualquier desarrollador web con mínima experiencia. Aunque algunos webmasters creen que están añadiendo una cuota de seguridad cuando limitan la longitud de caracteres en los campos TEXT y PASSWORD, esto no es así. Simplemente se guarda la página que contiene el formulario en el disco duro local, se abre con un editor web (o de textos) y se quitan los atributos MAXLENGTH="XX" de las etiquetas INPUT. Se guarda la página en el disco local, previa corrección del atributo ACTION del formulario para que apunte a la página ASP del sitio de acceso segurizado. Se ejecuta la página localmente en el explorador/navegador, se cargan los valores antedichos de usuario y clave, y listo.
En las pruebas realizadas en estos meses, traté de ingresar a un centenar de sitios segurizados a través de bases de datos mediante el método comentado. Cerca del 40% por ciento de estos sitios, muchos con largas páginas donde se comenta la privacidad de los usuarios, pudo ser vulnerado sin ningún esfuerzo. Por otro lado, como se comentó al principio, Macromedia Dreamweaver Ultradev 4 implementa el algoritmo que hace posible esta vulnerabilidad en la seguridad. Por lo tanto, todos los desarrolladores que utilizaron este popular programa para implementar la seguridad de sus sitios, mediante el "Server Behavior" de autentificación de usuarios, muy probablemente tengan este hueco de seguridad en sus sitios web. Algunos de los sitios que muestran esta falencia de seguridad poseen la certificación de seguridad estampada en sus páginas. No se si las empresas certificadoras de seguridad tienen algo de culpa. Estrictamente, se está trabajando en un entorno Secure Socket Layer (SSL). Cuando comienza la transacción segura, los códigos SQL ingresados en lugar de usuario/clave son encriptados y viajan "seguros" para que nadie pueda verlos con un sniffer y una llave de protocolos. Luego son desencriptados en el servidor seguro e impactan contra la base de datos, produciendo una transacción "seguramente" insegura.
Este artículo no tiene como objetivo enseñar a entrar a sistemas segurizados. Sólo estoy diciendo: - Hay un frecuente problema de seguridad y quizás su sitio lo posea. Si usted es desarrollador, trate de vulnerar con este método las zonas de sus sitios restringidas a usuarios registrados. Si su sitio es susceptible, a continuación le explicamos como solucionar el problema. Para corregir este defecto en el desarrollo del sistema de validación del usuario, hay que cambiar el algoritmo. Por ejemplo, el método planteado al principio (que hace posible el agujero de seguridad), puede reescribirse de la siguiente manera: * Se pide al usuario que ingrese el par usuario/clave en campos TEXT y PASSWORD, respectivamente, de un formulario en una página HTML convencional. * Al hacer click sobre el botón SUBMIT del formulario, estos datos son pasados a una página ASP que lo recibe. * La página ASP toma los datos de usuario y clave. * Se realiza una consulta SQL contra la base de datos para traer el registro que corresponde al USUARIO ingresado. * Si la consulta no devuelve ningún registro, el usuario ingresado no existe. Si devuelve un registro, se compara la clave ingresada en el formulario con la que existe en la base de datos. Si devuelve más de un registro es porque alguien trató de ingresar usando el método citado, pero no importa porque igual se comparará la clave ingresada (del tipo ' OR col_clave LIKE '%% ) con la presente en la base de datos, y estas no coincidirán (salvo que algún usuario extrañamente haya elegido tal código SQL como clave!). ¿Si? ¿No? Desconfíe, a lo mejor me equivoco. Sigo buscándole huecos a mi solución! Gustavo J. Ferrero |