Pinceladas sobre seguridad

Reconsideración de la seguridad en la configuración de las aplicaciones web

Bryan Sullivan

Bryan SullivanHace algunos años —antes de entrar a trabajar en Microsoft y junto al equipo de Ciclo de desarrollo de seguridad (SDL)— escribí un artículo acerca de los peligros asociados a configuraciones poco seguras en web.config y elaboré un listado de los 10 culpables más graves. Todavía puede encontrar este artículo: basta con que realice una búsqueda por “Top 10 Application Security Vulnerabilities in Web.Config Files” con su motor de búsqueda preferido. Las vulnerabilidades de configuración que discutí en aquel entonces siguen siendo pertinentes y serias hasta el día de hoy, aunque probablemente no presenten ninguna sorpresa para los lectores habituales de MSDN Magazine. Sigue siendo importante activar Custom Errors; sigue siendo importante desactivar las opciones de depuración y rastreo antes de llevar su aplicación a producción; y sigue siendo importante requerir SSL para las cookies de autenticación.

En la columna de este mes, quisiera retomar el punto donde el artículo anterior termina, y discutir algunos errores de configuración más oscuros pero igualmente serios. También echaré un vistazo a una nueva herramienta gratuita del equipo de Microsoft Information Security Tools llamado Web Application Configuration Analyzer que puede ayudarle a encontrar esos problemas. Recuerde que incluso la aplicación ASP.NET programada de la manera más segura puede caer víctima frente a un ataque si no está configurada correctamente.

EnableEventValidation

He podido observar que uno de los errores más comunes de los desarrolladores es darle a los usuarios una lista de opciones, y luego suponer que en verdad van a elegir alguno de esos valores. Pareciera ser bastante lógico: si agrega un control ListBox a una página y lo rellena con una lista de todos los estados de los Estados Unidos, esperaría recibir de vuelta “Washington” o “Georgia” o “Texas”. No esperaría “Foo” ni “!@#$%” ni “<script>alert(document.cookie);</script>”. Puede que no sea posible especificar ese tipo de valores usando la aplicación de modo tradicional con un explorador ¡pero existen muchas otras formas de acceder a una aplicación web que no requieren del uso de un explorador! Con una herramienta de proxy web, como por ejemplo Fiddler de Eric Lawrence (que sigue siendo una de mis herramientas favoritas para encontrar vulnerabilidades de seguridad en aplicaciones web, y que se puede descargar de fiddler2.com), puede enviar cualquier valor que quiera para cualquier campo de formulario. Si su aplicación no está preparada para esta posibilidad puede llegar a presentar errores potencialmente peligrosos.

La opción de configuración EnableEventValidation es un mecanismo de defensa en profundidad que le ayuda a defenderse contra este tipo de ataques. Si un usuario malintencionado trata de enviar un valor inesperado para un control que acepta una lista finita de valores (como un ListBox, pero no como un TextBox que ya acepta cualquier valor), la aplicación detecta la alteración y genera una excepción.

Malo:

<configuration>

  <system.web>

    <pages enableEventValidation="false"/>

Bueno:

<configuration>

  <system.web>

    <pages enableEventValidation="true"/>

PasswordFormat

El marco de proveedor de suscripciones proporcionado como parte de ASP.NET (a partir de ASP.NET 2.0) es una característica excelente que permite que los desarrolladores no tengan que volver a reinventar siempre de nuevo la funcionalidad del proveedor de suscripciones. Si se mantiene la configuración predeterminada, los proveedores integrados son bastante buenos, en general, desde el punto de vista de la seguridad. Pero si se realizan cambios en los valores de configuración de las suscripciones, pueden volverse significativamente menos seguros.

Un buen ejemplo de esto es la configuración de PasswordFormat, que determina cómo se almacenan las contraseñas de los usuarios. Cuenta con tres opciones: Clear, que almacena las contraseñas como texto simple; Encrypted, que cifra las contraseñas antes de almacenarlas; y Hashed, que almacena los valores hash de las contraseñas en vez de las contraseñas mismas. De estas opciones, Clear es lejos la peor. Nunca es adecuado almacenar las contraseñas como texto sin cifrar. Una opción mucho mejor es Encrypted, y la mejor opción es Hashed, ya que la mejor forma de guardar un secreto es no guardarlo en absoluto. Sin embargo, como no hay forma de recuperar la contraseña original de un valor hash, si un usuario llega a olvidar su contraseña, no habrá forma de recuperarla.

Malo:

<configuration>

  <system.web>

    <membership>

      <providers>

        <clear/>

        <add name="AspNetSqlMembershipProvider" 

             passwordFormat="Clear"

             ...

 />

Mejor:

<configuration>

  <system.web>

    <membership>

      <providers>

        <clear/>

        <add name="AspNetSqlMembershipProvider" 

             passwordFormat="Encrypted"

             ...

 />

Óptimo:

<configuration>

  <system.web>

    <membership>

      <providers>

        <clear/>

        <add name="AspNetSqlMembershipProvider" 

             passwordFormat="Hashed"

             ...

 />

MinRequiredPasswordLength y MinRequiredNonalphanumericCharacters

En la configuración de suscripción hay dos valores predeterminados que se deberían cambiar: las propiedades MinRequiredPasswordLength y MinRequiredNonalphanumericCharacters. Para los objetos AspNetSqlMembershipProvider, de forma predeterminada estas configuraciones exigen un largo de seis caracteres para las contraseñas como mínimo, y no exigen el uso de caracteres no-alfanuméricos. Para mejorar la seguridad, se deberían usar opciones de configuración mucho más estrictas. Usted debería exigir contraseñas de al menos 10 caracteres, y que contengan dos o más caracteres no alfanuméricos. Un mínimo de 14 caracteres con cuatro o más caracteres no alfanuméricos sería mejor aún.

Es verdad que el largo y la complejidad de las contraseñas son un arma de doble filo: al exigirle a los usuarios el uso de contraseñas más largas y complejas disminuye la probabilidad que esas contraseñas sean afectadas por un ataque de fuerza bruta, pero también aumenta la probabilidad que los usuarios no puedan recordar sus contraseñas, y se vean obligados a anotarlas en papel. Sin embargo aun cuando esto pueda sonar como una posible brecha de seguridad terrible, muchos expertos en seguridad creen que los beneficios superan los riesgos. El conocido gurú de la seguridad Bruce Schneier, por nombrar uno, sugiere que los usuarios creen contraseñas largas y complejas, y las guarden en la cartera, por ser un lugar donde las personas acostumbran guardar y proteger papeles pequeños.

Malo:

<configuration>

  <system.web>

    <membership>

      <providers>

        <clear/>

        <add name="AspNetSqlMembershipProvider"

             minRequiredPasswordLength="6"

             minRequiredNonalphanumericCharacters="0"

             ...

 />

Bueno:

<configuration>

  <system.web>

    <membership>

      <providers>

        <clear/>

        <add name="AspNetSqlMembershipProvider" 

             minRequiredPasswordLength="14"

             minRequiredNonalphanumericCharacters="4"

             ...

 />

También el sitio Microsoft Online Safety (microsoft.com/protect/fraud/passwords/create.aspx) sugiere que los usuarios anoten sus contraseñas, y además contiene información acerca de cómo crear contraseñas seguras y cómo protegerlas.

ValidateRequest

El Cross-site scripting (XSS) sigue siendo la vulnerabilidad más común en la web. Un informe publicado en julio por Cenzic Inc. encontró que en el primer semestre de este año las vulnerabilidades XSS dieron cuenta del 28% de todos los ataques en la web. Dadas las consecuencias potencialmente graves de una vulnerabilidad XSS (frecuentemente he oído llamar al XSS el “desbordamiento de búfer de la web”) es lógico que los desarrolladores hagan todo lo que está en sus manos para ayudar a defender sus aplicaciones contra este tipo de ataques. Resulta particularmente agradable si se consigue una defensa que básicamente no cuesta nada, y precisamente eso es ValidateRequest.

Malo:

<configuration>

  <system.web>

    <pages validateRequest="false" />

Bueno:

<configuration>

  <system.web>

    <pages validateRequest="true" />

ValidateRequest funciona buscando la presencia de patrones comunes de ataque en las entradas del usuario, tales como corchetes angulares (<) en las cadenas. En el caso de encontrarlos, la aplicación genera una excepción y detiene el procesamiento de la solicitud. Si bien esto no ofrece una solución completa en sí mismo (ya que además siempre debería aplicar una lógica de codificación de salida y una validación y protección de las entradas, tal como la que está integrada en Microsoft Web Protection Library) ValidateRequest sí bloquea varios tipos populares de ataques XSS. Lo mejor es dejar ValidateRequest habilitado siempre cuando sea posible.

MaxRequestLength

Por lo general no es bueno permitir que los usuarios envíen solicitudes HTTP de tamaño indefinido a su aplicación. Si lo hace, queda abierto a ataques de denegación de servicio (DoS), donde un usuario solo puede aprovechar la posibilidad de agotar todo su ancho de banda, sus ciclos de procesador y el espacio en disco duro, impidiendo que su aplicación esté disponible para sus usuarios legítimos.

Para ayudar a prevenir esto puede establecer un valor adecuadamente pequeño para la propiedad MaxRequestLength. El valor predeterminado es 4096 KB (4 MB). Como cada aplicación tiene sus propios requerimientos acerca de qué son tamaños de requerimientos corrientes y excepcionales, es difícil entregar un valor para MaxRequestLength que sirva como regla general. Así que en vez de entregar ejemplos de lo que serían configuraciones “buenas” o “malas”, simplemente sugiero que tenga en cuenta el hecho que mientras más alto el valor que establece para esta propiedad, mayor es el riesgo de exponerse a un ataque DoS:

<configuration>

  <system.web>

    <httpRuntime maxRequestLength="4096"/>

EnableViewStateMac

Previamente, en la columna Pinceladas sobre seguridad de julio 2010 sobre la seguridad del estado de vista (msdn.microsoft.com/magazine/ff797918), ya escribí acerca de la opción de configuración EnableViewStateMac. Por si usted se perdió esa columna, EnableViewStateMac es una defensa para prevenir que los atacantes alteren los estados de vista del lado del cliente. Cuando EnableViewStateMac está habilitado, las aplicaciones ASP.NET agregan un código de autenticación de mensajes (MAC) cifrado al valor del formulario oculto __VIEWSTATE. Un atacante no puede determinar una MAC válida para un ataque arbitrario (por ejemplo, intentando envenenar el estado de vista de una víctima para inyectar un JavaScript malicioso). Por lo tanto, si un atacante intenta alterar el estado de vista de esta forma, se invalida la MAC, y la aplicación ASP.NET bloquea la solicitud.

Malo:

<configuration>

  <system.web>

    <pages enableViewStateMac="false"/>

Bueno:

<configuration>

  <system.web>

    <pages enableViewStateMac="true"/>

Si está implementando su aplicación en un entorno de granja de servidores, también es importante recordar especificar manualmente una clave para la MAC en vez de dejar que la aplicación genere claves aleatorias de manera automática (si no especifica las claves manualmente, cada máquina en la granja generará una clave diferente, y la MAC del estado de vista creada por cualquier equipo será considerada no válida por todos los demás equipos y será bloqueada).

Además hay algunas pautas adicionales que debiera seguir al crear las claves manualmente para asegurar un máximo de seguridad para su estado de vista. Primero, asegúrese de especificar uno de los algoritmos de cifrado aprobados dentro del SDL. Para aplicaciones que usen el Microsoft .NET Framework 3.5 o anteriores, esto significa usar SHA1 (que es el algoritmo predeterminado) o AES. Para aplicaciones que usen .NET Framework 4, también puede usar HMACSHA256, HMACSHA384 o HMACSHA512. Evite algoritmos débiles tales como MD5.

Malo:

<configuration>

  <system.web>

    <machineKey validation="MD5" validationKey="..."/>

Bueno:

<configuration>

  <system.web>

    <machineKey validation="AES" validationKey="..."/>

Tan importante como la elección de un algoritmo fuerte es la elección de una clave segura. Use un generador de números aleatorios criptográficamente sólido para generar claves de 64 bytes (o 128 bytes si emplea los algoritmos HMACSHA384 o HMACSHA512). Puede encontrar un código de ejemplo para generar claves adecuadas en la columna Pinceladas sobre seguridad de julio 2010 que mencioné anteriormente.

Malo:

<configuration>

  <system.web>

    <machineKey validation="AES" validationKey="12345"/>

Bueno:

<configuration>

  <system.web>

    <machineKey validation="AES" validationKey="143a907bb73069a2fe7c..."/>

ViewStateEncryptionMode

Así como usa una MAC en estado de vista de su aplicación para evitar pueda ser alterado por posibles atacantes, también debería cifrar el estado de vista para evitar que éstos lo puedan leer. A menos que esté 100 por ciento seguro que ninguno de sus estados de vista contiene información confidencial, lo más seguro es usar la propiedad ViewStateEncryptionMode para protegerlos mediante la opción de cifrado.

Malo:

<configuration>

  <system.web>

    <pages viewStateEncryptionMode="Never"/>

Bueno:

<configuration>

  <system.web>

    <pages viewStateEncryptionMode="Auto"/>

Al igual que con EnableViewStateMac, también aquí tiene la opción de varios algoritmos de cifrado que la aplicación puede emplear para cifrar el estado de vista. Sin embargo, lo mejor es quedarse con AES, que es el único algoritmo aprobado por los estándares de cifrado dentro del SDL.

Malo:

<configuration>

  <system.web>

    <machineKey decryption="DES" decryptionKey=""/>

Bueno:

<configuration>

  <system.web>

    <machineKey decryption="AES" decryptionKey=""/>

Finalmente, recuerde que si está implementando su aplicación en una granja de servidores, deberá especificar una clave manualmente. Asegúrese de establecer la clave a un valor criptográficamente aleatorio de 24 bytes.

Malo:

<configuration>

  <system.web>

    <machineKey decryption="AES" decryptionKey="12345"/>

Bueno:

<configuration>

  <system.web>

    <machineKey decryption="AES" decryptionKey="143a907bb73069a2fe7c..."/>

UseUnsafeHeaderParsing

Frecuentemente, cuando un desarrollador se encuentra muy frustrado con un error difícil termina por implementar cualquier cambio del que ha leído sin entender verdaderamente los efectos que éste tiene en su aplicación. La configuración UseUnsafeHeaderParsing es un buen ejemplo de este fenómeno. Si bien la palabra “unsafe” (no seguro) en el nombre de la propiedad debería bastar para poner a la mayoría de las personas en alerta, una búsqueda rápida en Internet entrega literalmente miles de resultados que muestran que los desarrolladores igual habilitan esta propiedad. Si usted habilita UseUnsafeHeaderParsing, su aplicación pasará por alto muchas especificaciones RFC de HTTP e intentará analizar solicitudes no válidas. Si bien es posible que esta configuración le permita a su aplicación funcionar con clientes HTTP que no obedecen a los estándares HTTP (esto es la razón por la cual tantas personas sugieren esto como una solución), también puede dejar su aplicación expuesta a ataques por encabezados no válidos. Váyase a lo seguro, y no habilite esta opción.

Malo:

<configuration>

  <system.net>

    <settings>

      <httpWebRequest 

       useUnsafeHeaderParsing=

       "true"/>

Bueno:

<configuration>

  <system.net>

    <settings>

      <httpWebRequest 

       useUnsafeHeaderParsing=

       "false"/>

Web Application Configuration Analyzer (WACA)

Ahora que hemos echado un vistazo a algunas opciones de configuración peligrosas, revisaremos una herramienta que le puede ayudar a encontrar de manera automática estas configuraciones en su código. Porque si bien es verdad que la revisión manual de código puede ser útil, un análisis automático puede ser más minucioso y más coherente. Esto también lo liberará la monotonía de tener que revisar los archivos XML a mano y le dejará más tiempo libre para solucionar los problemas más interesantes.

El equipo de Microsoft Information Security Tools ha lanzado algunas herramientas de seguridad excelentes, incluyendo dos (AntiXSS/Web Protection Library y CAT.NET) que internamente ahora se exigen de manera obligatoria para todos los productos y servicios .NET Framework de Microsoft como parte del Microsoft SDL. El último producto, WACA, está diseñado para detectar posibles configuraciones erróneas, tales como las que describí en este artículo y en el anterior sobre las 10 vulnerabilidades más comunes en web.conf. Algunos controles que realiza WACA son por ejemplo:

  • ¿Está habilitado el seguimiento?
  • ¿Es demasiado grande el valor de MaxRequestLength?
  • ¿Están habilitadas las cookies HttpOnly?
  • ¿Se requiere SSL para el inicio de sesión para la autenticación de formularios?
  • ¿EnableViewStateMac está establecido en false?

Además, WACA es capaz de controlar errores de configuración en el mismo IIS, además de errores de configuración en las bases de dato SQL y de problemas en el nivel de sistema. Como por ejemplo:

  • ¿Está deshabilitado el servicio Firewall de Windows?
  • ¿El administrador local se llama “Administrator”?
  • ¿El archivo de registro de IIS se encuentra en la unidad del sistema?
  • ¿Está habilitada la ejecución en el directorio virtual de la aplicación?
  • ¿Se encuentran bases de dato de ejemplo en el servidor SQL?
  • ¿Está habilitado xp_cmdshell en el servidor SQL?

Mientras que los desarrolladores y evaluadores usarán WACA principalmente para revisar la configuración de sus aplicaciones, los administradores de sistema y de bases de datos sacarán provecho de WACA para revisar las configuraciones de IIS, SQL y de sistema (vea figura 1). En conjunto, WACA realiza más de 140 comprobaciones basadas en requisitos de SDL, patrones y prácticas y directrices de programación.

image: Web Application Configuration Analyzer Rules

Figura 1 Reglas de Web Application Configuration Analyzer

Otra característica de gran utilidad de WACA es que permite automatizar la creación de elementos de trabajo o errores en los proyectos de equipo de Team Foundation Server (TFS) basándose en los resultados del examen de WACA. Esto resulta particularmente útil cuando se usa con un proyecto de equipo creado, ya sea con la plantilla de procesos de SDL, o con la plantilla de procesos de MSF-Agile+SDL. En la página de instalación de WACA TFS asigne el campo de la plantilla “Origin” al valor “Web Application Configuration Analyzer”. Ahora, cuando revise los informes de error y gráficos de tendencia podrá filtrar y explorar meticulosamente los resultados de WACA para ver qué tan eficaz ha sido en detectar posibles vulnerabilidades (vea la figura 2).

image: WACA Team Foundation Server Integration

Figura 2 Integración de WACA Team Foundation Server

Puede obtener más información acerca de WACA en la página del grupo Microsoft IT InfoSec (msdn.microsoft.com/security/dd547422); ver una demostración en vídeo de la herramienta presentada por Anil Revuru, jefe de programas del proyecto WACA (msdn.microsoft.com/security/ee909463); o, mejor aún, descargar la herramienta y probarla por su cuenta (tinyurl.com/3x7bgfd).

Siempre compruebe sus configuraciones

Es muy frustrante pensar que puede desarrollar su aplicación siguiendo todas las directrices de desarrollo seguro y prácticas recomendadas, y aun así caer víctima de un ataque por causa de un sencillo error en un archivo de configuración web.config. Es más frustrante aun cuando se da cuenta que los archivos web.config fueron diseñados para ser cambiados en cualquier momento, y que el error de configuración podría producirse años después de que haya terminado de programar la aplicación y la haya movido a producción. Es importante revisar siempre los valores de configuración. No solamente con una inspección manual, sino que también usando herramientas automatizadas; y no sólo durante el ciclo de desarrollo, sino que también en producción.

Continuación acerca de ataques DoS con expresiones regulares

En un tema completamente diferente: en la columna Pinceladas sobre seguridad de mayo 2010 (msdn.microsoft.com/magazine/ff646973), traté los ataques DoS con expresiones regulares presentados por Checkmarx en el congreso OWASP Israel en septiembre del año 2009. En esa columna también entregué el código para un comprobador aleatorio para un DoS con expresiones regulares basado en la funcionalidad Data Generation Plan de Visual Studio Database Projects. Aunque técnicamente el método era sólido y detectaba bien este tipo de vulnerabilidades, debo reconocer que resultaba un tanto tedioso generar los datos de prueba, y requería poseer una licencia de Visual Studio Database Projects. Así que estoy encantado de poder anunciar que el equipo SDL ha lanzado una nueva herramienta (que se puede descargar gratuitamente) para realizar pruebas aleatorias para vulnerabilidades de expresiones regulares. La herramienta se hace cargo de los pormenores de la generación de datos, y no tiene dependencias externas (más que .NET Framework 3.5). La herramienta se muestra en la figura 3.

image: SDL Regex Fuzzer

Figura 3 SDL Regex Fuzzer

Puede descargar SDL Regex Fuzzer de microsoft.com/sdl. Pruébelo y cuéntenos qué le parece.      

Bryan Sullivan es administrador del programa de seguridad del equipo de ciclo de vida de desarrollo de seguridad de Microsoft, donde se especializa en aplicaciones web y problemas de seguridad de Microsoft .NET Framework. Es el autor de “Ajax Security” (Addison-Wesley, 2007).

Gracias al siguiente experto técnico por su ayuda en la revisión de este artículo: Anil Revuru