2010-05-26 11 views
19

Normalmente, cuando un sitio requiere que haya iniciado sesión antes de poder acceder a una página determinada, está llevado a la pantalla de inicio de sesión y después de autenticarse con éxito, se le redirige a la página solicitada originalmente. Esto es ideal para la usabilidad, pero sin un escrutinio cuidadoso, esta característica puede convertirse fácilmente en una vulnerabilidad open redirect.Cómo evitar la vulnerabilidad de redireccionamiento abierto y redirigir de forma segura al inicio de sesión exitoso (SUGERENCIA: el código predeterminado de ASP.NET MVC 2 es vulnerable)

Lamentablemente, para un ejemplo de esta vulnerabilidad, no busque más que la acción de conexión predeterminada proporcionada por ASP.NET MVC 2:

[HttpPost] 
public ActionResult LogOn(LogOnModel model, string returnUrl) 
{ 
    if (ModelState.IsValid) { 
     if (MembershipService.ValidateUser(model.UserName, model.Password)) { 
      FormsService.SignIn(model.UserName, model.RememberMe); 

      if (!String.IsNullOrEmpty(returnUrl)) { 
       return Redirect(returnUrl); // open redirect vulnerability HERE 
      } else { 
       return RedirectToAction("Index", "Home"); 
      } 

     } else { 
      ModelState.AddModelError("", "User name or password incorrect..."); 
     } 
    } 

    return View(model); 
} 

Si un usuario se autentica correctamente, se les redirige a "ReturnURL" (si fue proporcionado a través del formulario de ingreso).

Aquí es un simple ataque de ejemplo (uno de muchos, en realidad) que explota esta vulnerabilidad:

  1. El atacante, que pretende ser el banco de la víctima, envía un correo electrónico a la víctima que contiene un enlace, así: http://www.mybank.com/logon?returnUrl=http://www.badsite.com
  2. Habiendo aprendido a verificar el nombre de dominio ENTERO (por ej., Google.com = BUENO, google.com.as31x.example.com = MALO), la víctima sabe que el enlace está bien - no hay ningún subdominio complicado phishing pasando.
  3. La víctima hace clic en el enlace, ve su sitio web real de banca familiar y se le pide que iniciar sesión
  4. Víctima inicia la sesión y, posteriormente, se redirige a http://www.badsite.com que se hace para parecerse exactamente a la página web del banco de la víctima, por lo que la víctima no lo hace sabe que ahora está en un sitio diferente.
  5. http://www.badsite.com dice algo así como "Tenemos que actualizar nuestros registros: ingrese alguna información extremadamente personal a continuación: [ssn], [dirección], [número de teléfono], etc."
  6. La víctima, aún pensando que es en su sitio web de banca, cae en la trampa y proporciona atacante con la información

Cualquier ideas sobre cómo mantener esta redirección-on-éxito-login funcionalidad sin embargo, evitar el abierto redirigir la vulnerabilidad?

Me inclino por la opción de dividir el parámetro "returnUrl" en partes de controlador/acción y usar "RedirectToRouteResult" en lugar de simplemente "Redirigir". ¿Este enfoque abre nuevas vulnerabilidades?

actualización

Por limitándome al controlador rutas/acción, no se puede redirigir a rutas personalizadas (por ejemplo /backend/calendar/2010/05/21). Sé que al pasar más parámetros a la acción LogOn, I podría hacer que funcione, pero siento que siempre volveré a visitar este método, manteniéndolo actualizado con nuestro esquema de enrutamiento. Entonces, en lugar de dividir returnUrl en sus partes de controlador/acción, mantengo returnUrl tal como está y lo analizo para asegurarme de que contiene solo una ruta relativa (por ejemplo, /users/1) y no una ruta absoluta (por ejemplo, http://www.badsite.com/users/1).Aquí está el código que estoy usando:

private static bool CheckRedirect(string url) { 
    try { 
     new Uri(url, UriKind.Relative); 
    } 
    catch (UriFormatException e) { 
     return false; 
    } 

    return true; 
} 

Nota al margen: Sé que esto re-direccionamiento abierto puede no parecer una gran cosa en comparación con los gustos de XSS y CSRF, pero nosotros los desarrolladores son la única cosa proteger a nuestros clientes de los malos: cualquier cosa que podamos hacer para dificultar el trabajo de los malos es una victoria en mi libro.

Gracias, Brad

+0

Si falla la memoria, ReturnURL debe coincidir con uno de sus rutas, o la cosa se YSOD. Pero podría estar equivocado. ¿Has probado esto para demostrar que es una vulnerabilidad? –

+0

Acabo de probarlo. Es verdad. Otra pregunta relacionada [aquí] (http://stackoverflow.com/questions/2782710/what-is-it-about-the-default-asp-net-mvc-accountcontroller-code-that-some-hate) –

+0

Si te hará sentir mejor, estoy bastante seguro de que el 'Redirect' no * hace * un POST, sino un GET solamente. Pero supongo que eso no te ayuda si la información confidencial también se transmite en la URL de redireccionamiento como parámetros. –

Respuesta

0

Como siempre y cuando utilice una de las variantes de redirección que utiliza parámetros de regulación y de acción o un nombre de ruta, debe estar bien, siempre y cuando tenga los controles de seguridad adecuados sobre sus métodos de controlador.

El concepto es que, sea lo que sea que use para su redireccionamiento, debe pasar por el motor de enrutamiento y validarse haciendo coincidir una ruta.

Pero sospecho que la vulnerabilidad real es Cross-Site Scripting. A menos que su usuario malintencionado pueda inyectar algo de Javascript en la página, no podrá manipular la URL de devolución ni ninguno de sus parámetros (ya que, de lo contrario, controlará todo el código del servidor y del navegador).

+1

Por favor, mira mi actualización: elegí analizar el returnUrl, asegurándome de que no es una URL absoluta. –

1

Siempre puede mantener un registro de la página anterior con TempData cuando el usuario no está autenticado y usar eso para redirigir a la página anterior en lugar de un parámetro url.

+0

No creo que esto funcione, TempData solo persiste en 1 redirección. En este caso, una persona intenta golpear una URL segura que hace que su navegador redirija al inicio de sesión. Después de escribir el nombre de usuario/contraseña, inician sesión y se les redirige a la página solicitada originalmente. Esto es dos redireccionamientos, por lo que TempData no puede funcionar. ¿Una mejor idea podría ser adjuntar la URL de redireccionamiento a la sesión? –

3

Sí esto es una vulnerabilidad. Antes de redirigir, debe inspeccionar el parámetro de cadena returnUrl pasándolo a un objeto Uri y asegúrese de que el dominio objetivo sea el mismo que el dominio solicitante. También debe tener en cuenta el caso cuando returnUrl es una dirección relativa como /admin. No hay problema en este caso, ya que la redirección será a la misma aplicación.

+0

Gracias por esta sugerencia, parece estar funcionando bien hasta ahora. Consulte mi actualización a la pregunta original para obtener detalles sobre cómo estoy analizando el returnUrl. –

9

Jon Galloway escribió un article con una solución para MVC 2 (y 1).

He aquí el fragmento que debe ayudar con su problema:

private bool IsLocalUrl(string url) 
{ 
    if (string.IsNullOrEmpty(url)) 
    { 
     return false; 
    } 

    Uri absoluteUri; 
    if (Uri.TryCreate(url, UriKind.Absolute, out absoluteUri)) 
    { 
     return String.Equals(this.Request.Url.Host, absoluteUri.Host, 
        StringComparison.OrdinalIgnoreCase); 
    } 
    else 
    { 
     bool isLocal = !url.StartsWith("http:", StringComparison.OrdinalIgnoreCase) 
      && !url.StartsWith("https:", StringComparison.OrdinalIgnoreCase) 
      && Uri.IsWellFormedUriString(url, UriKind.Relative); 
     return isLocal; 
    } 
} 
+0

Esto es, por supuesto, similar a su actualización, sin el costo de atrapar una excepción. –

Cuestiones relacionadas