Control de autorizaciones en aplicaciones web: vulnerabilidades típicas

Dentro de las vulnerabilidades de las aplicaciones web, algunas de las más frecuentes se encuentran asociadas al modo en que la aplicación decide si un usuario está autorizado a ejecutar una determinada función o acceder a un determnado recurso (ej: un documento). El escenario de mayor riesgo es cuando un atacante es capaz de explotar estas vulnerabilidades y llega a ser capaz de llevar a cabo acciones fraudulentas críticas como crear un usuario privilegiado en el sistema, acceder a información restringida o realizar operaciones bancarias no autorizadas, entre otras. En este artículo vamos a repasar algunos de los errores de programación más frecuentes y perfilaremos algunas recomendaciones para evitarlos. Se asume que el lector tiene unas nociones mínimas sobre cómo funcionan las aplicaciones web.

BURP_01

Seguridad por oscuridad

Aunque pueda parecer mentira, ésta es una estrategia de seguridad cuyo uso se puede evidenciar en un buen número de aplicaciones. Por ejemplo, cuando una URL que da acceso a una página que permite ejecutar una función sensible o acceder a un recurso es «conocida solamente por unas pocas personas» autorizadas. Dado que la URL no aparece visible en ninguna página, ni está enganchada a ningún menú, hiperenlace o botón de acción; se asume que nadie que no esté autorizado sabrá llegar a esta página «secreta». En algunas ocasiones, además, se usa una nomenclatura poco descriptiva para que inlcuso si alguien no autorizado ve la URL «no pueda deducir fácilmente» el uso de la misma. Obviamente, en casos extremos, la aplicación no incorpora ningún mecanismo adicional que permita decidir si alguien está o no autorizado; se asume que si conoce la URL de la página es poque es una de las personas autorizadas con las que se ha compartido este «secreto». Obviamente esta es una práctica de alto riesgo, especialmente si la funcionalidad o los recursos protegidos de esta manera son sensibles o críticos. No hay que olvidar que existe una gran variedad de herramientas ampliamente difundidas que permiten a un usuario interesado en ello, analizar e incluso descargarse en poco tiempo todas y cada una de las páginas existentes en un sitio web. A partir de aquí, con un poco de diligencia por parte de un hacker o un analista de aplicaciones, es sólo cuestión de tiempo que alguien desvele el pretendido secreto oculto y que haga uso indebidamente del sistema. La filosofía de seguridad por oscuridad, usada como única barrera de protección, es siempre una mala idea.

Control a nivel de Cliente

Un sistema un poco más sofisticado pero igualmente arriesgado es el de usar scripts de cliente (léase java Script) para que, en función del perfil del usuario conectado, se muestre o no una pantalla o un medio (ej: un menú o botón de acción) de acceso a la funcionalidad protegida. Este es un mecanismo muy sencillo de implementar, pero presenta un problema bastante importante: el java Script que controla lo que el usuario puede ver o hacer en la aplicación ha sido descargado a su navegador local y el código fuente es accesible (y manipulable) por el usuario final. Esta es una de las primeras características de la aplicación que será analizada por un hacker que trate vulnerar la funcionalidad del sistema. Una vez verificado este funcionamiento, existen herramientas que permitirán al atacante sortear con facilidad el frágil filtro de la función java Script y hacer visibles los elementos que el script ocultaba. El uso de scripts de cliente (que se ejecutan en el navegador del dispositivo del usuario) como único mecanismo para articular la seguridad de la aplicación es una mala práctica a evitar.

function IsCurrentUserHasContribPerms() 
{
  IsCurrentUserMemberOfGroup("Members", function (isCurrentUserInGroup) {
    if(isCurrentUserInGroup)
    {
        // The current user is in the [Members] group!
        //Mostrar el menú para miembros
    }
  });

}

Control de la sesión desde el dispositivo de usuario

Una categoría particular de los problemas que supone el control a nivel de cliente hace referencia al modo en que se maneja el contexto de sesión de la aplicación. El objetivo del contexto de sesión es poder mantener información relativa al usuario conectado a lo largo de todo el tiempo que transcurre desde que entra en la aplicación web, hace lo que deba hacer y finalmente sale de la aplicación. Esto permite no tener que estar haciendo constamente consultas a la base de datos con cada página visitada y debe ser gestionado por la aplicación ya que por defecto, el servidor web no está preparado nada mas que para servir páginas al cliente y olvedarse de él de inmediato. Existen distintas estrategias a la hora de gestionar el contexto de sesión de una aplicación web, siendo uno de los más fáciles de implementar, más usados y lamentablemente más inseguro el de almacenar la información relevante de la sesión a nivel del dispositivo del usuario en su navegador web. Con este modelo toda la información relevante para determinar lo que el usuario puede o no hacer o acceder (por ej: el conocimiento sobre si el usuario se ha validado satisfactoriamente, si el usuario tiene o no permisos de administración, si su suscripcion está o no expirada, el saldo disponible en su cuenta, el contenido del carrito de la compra, etc…) se almacena en cookies o en campos ocultos de los formularios web que se muestran en pantalla. Esta información se va pasando de formulario a formulario a medida que el usuario navega, para preservar de esta manera esta información a lo largo del flujo de navegación. Cuando el usuario tiene que desencadenar una acción, envía hacia el servidor la petición junto con la información relevante almacenada en estos campos o en la cookie para que la aplicación que se ejecuta en el servidor pueda determinar si el usuario puede acceder o no.

Usaremos un ejemplo para clarificar mejor este concepto: Supongamos que cuando el usuario se valida en la aplicación, ésta verifica que el usuario tiene perfil de administrador del sistema y envía hacia el navegador del usuario esta información que queda almacenada en un campo oculto llamado ‘IS_ADMIN’ que contiene el valor ‘VERDADERO’. Supongamos que entonces el usuario selecciona una opción de menú que le dará acceso a una página de configuración de la aplicación. En ese momento el navegador envía una petición al servidor para solictar dicha página y enviará como parámetro de la petición al servidor, el valor que hay almaceado en el campo oculto is_admin:

URL = http://miservidor.com/pagina?is_admin=true

Es en este escenario la aplicación del servidor evalúa el valor enviado por parámetro en la URL (o por método POST) y al verificar que el valor es ‘true’, entonces devueve la página solicitada al navegador del cliente ya que da po supuesto que el usuario es efectivamente un administrador. Obviamente, con este rudimentario y chapudcero método, a nadie se le debería escapar que será muy fácil engañar a la aplicación y acceder a las páginas de administración. Lo único que un usurario malicioso tendría que hacer es manipular el valor del campo oculto o simplemente asignar manualmente el valor del parámetro de la URL y el servidor creerá que el atacante es un administrador del sistema. Aunque pueda parecer mentira, este tipo de vulnerabilidades suele verse en aplicaciones reales!!!.

Accediendo a recursos a través de identificadores fácilmente predecibles

Una práctica muy extendida y que frecuentemente genera incidentes de privacidad, es aquella que permite acceder al usuario a recursos alacenados en el servidor como pueden ser archivos confidenciales de diversa índole (ej: contratos, informes, facturas, archivos multimedia…) a través de una página web que recibe como parámetro un código identificador del documento. En estos casos, el identificador suele ser para mayor gloria un código fácilmente predecible (ej: un simple secuencial). La pretendida seguridad de éste método viene determinada porque la URL que permite acceder al recurso se genera dinámicamente y se muestra solamente al usuario conectado. El problema de este sencillo mecanismo es que cualquiera que analice el comportamiento de la aplicación se dará cuenta de que si uno accede reiteradamente a la página, sin estar aun siquiera validado en el sistema, y se le van pasando parámetros de prueba siguiendo el orden numérico secuencial (tano si se hace por método GET o POST), entonces es trivial descargase los documentos pretendidamente privados de todos los usuarios del sistema.

URL = http://miservidor.com/ver_documento.php?doc_id=00000000001;
URL = http://miservidor.com/ver_documento.php?doc_id=00000000002;
URL = http://miservidor.com/ver_documento.php?doc_id=00000000003;

Aún en el caso de que la aplicación sea ligeramente más sofisticada y el parámetro doc_id se pase no en la URL sino por el método POST, la tarea del atacante no será muy complicada haciendo uso de una sencilla herramienta que le permita manipular los valores pasados al servidor en la llamada a la página.

BURP_02Figura: La famosa herramienta Burp Suite incorpora un módulo que permite la manipulación de los parámetros de la llamada web antes de ser enviada al servidor, sorteando los controles de la aplicación y el navegador

En el ejemplo que hemos planteado, se pueden observar algunos de los errores de base comunes a los distintos escenarios que estamos revisando:

  • Suponer que todos los usuarios del sitio web son legítimos y con buenas intenciones, que usarán para acceder a la aplicación un sencilllo navegador web y que seguirán el flujo de navegación predefinido y esperado por el programador usando los accesos (menús, botones, hiperenlaces) que se muestran en la aplicación.
  • Recibir parámetros de entrada desde el navegador del cliente que permiten acceder a recursos o funciones sensibles del sistema, sin validar ni cuestionar la legitimidad de estas peticiones ni que los valores enviados puedan estar manipulados de manera maliciosa.

Protección de acceso mediante el Referer

Otra forma habitual de controlar el acceso a una página o recurso en la web es verificando el referer de la página; es decir, comprobando cual es la página inicial desde la cual el usuario ha llegado a la página de destino, con el objeto de permitir el acceso sólo si se procede de una determinada página o dominio (por ejemplo, si se ha llegado desde la página de administración de la aplicación). Este control es relativamente fácil de implementar ya que el referer se puede consultar en la cabecera de la llamada HTTP. Esta es una práctica que puede ser útil si se combina con otros controles adicionales; sin embargo, por sí sola presenta alunos problemas ya que existen herramientas que permiten la manipulación fraudulenta de las cabeceras HTTP antes de ser enviadas al servidor; de modo que un usuario malicioso puede engarñar al servidor haciendole cree que viene de una página o dominio concretos.

BURP_01

Recomendaciones

A continuación referimos algunas directrices básicas a tener en cuenta. Hay que recordar que el primer paso es ser consciente del riesgo / criticidad asociado en cada caso concreto y valorar de acuerdo a ello, el impacto que cada decisión de diseño tiene en la seguridad de la aplicación:

  • Seguridad por oscuridad: Repetimos, no es fáctible. Ningún modelo de seguridad se puede basar en este principio.
  • No asumir nunca que los usuarios vayan a seguir un determinado flujo de navegación. Los usuarios maliciosos acceden normalmente fuera de toda supuesta lógica y usan herramientas que les ayudan a ello.
  • Mantener las variables relevantes relacionadas con el contexto de sesión, siempre a nivel del servidor, de modo que estos valores no sean directamente manipulables por un usuario malicioso. Nunca confiar ni dar por bueno ningún parámetro crítico que provenga directamente del navegador del cliente, sin hacer validaciones adicionales en el lado del servidor. Por definición los parámetros de cliente pueden ser fácilmente manipulados.
  • Evitar enviar desde el navegador del usuario (ni por POST ni por GET) parámetros que representen valores que puedan ser validados / obtenidos directamente por el servidor de manera más fiable; por ejemplo, en vez de enviar un número de cuenta sobre el que hacer el cargo de una compra, enviar al servidor un alias/indice que permita al servidor deducir cual de las cuentas almacenadas en la base de datos debe ser usada.
  • Sólo se puede confiar plenamente en los datos calculados / almacenados a nivel de servidor; si es necesario enviar datos desde el cliente, aquellos que sean más críticos devebrán volver a ser validados por el servidor desde una vía indirecta; por ejemplo, si como parámetro se envía el id de un documento para que sea mostrado, entonces chequear en el servidor si el identirficador enviado se corresponde con el de un documento para el que el usuario está autorizado.
  • En flujos de navegación complejos donde el usuario ha de pasar por una serie sucesiva de pantallas, realizar validaciones de las autorizaciones en cada uno de los pasos del flujo de navegación. Evitar suponer que, si se ha llegado a una determinada página que requiere un validacion previa en una página anterior, entonces el usuario está validado.
  • Si es encesario hacer validaciones en el lado del cliente con programas en javaScript, ofuscar el código de modo que no sea fácilmente accedido por usuarios no autorizados.

Con esto nos despedimos por hoy, un fuerte abrazo y hasta pronto.