Código para validar la URL de redireccionamiento

2

Mi aplicación web me permite indicar al formulario de inicio de sesión que redirija a una página determinada en el sitio web después de iniciar sesión correctamente. Por ejemplo, si el usuario va a la url http://localhost/login.php?returnto=%2FconfirmEmail.php , luego de completar el proceso de inicio de sesión, se redirige automáticamente a http://localhost/confirmEmail.php .

Después de completar el proceso de inicio de sesión, usé el siguiente código para validar la URL:

function isValidReturnURL( $input ) {
    if ( !is_string( $input ) ) { //Input is not a string
        return FALSE;
    }
    $cleanString = trim( urldecode( $input ) );
    if ( !strlen( $cleanString ) ) { //Input is an empty string
        return FALSE;
    }
    //Edit: I updated the following code (see below)
    $urlHost = parse_url( $cleanString, PHP_URL_HOST );
    if ( !empty($urlHost) ) { //All local urls will be relative
        return FALSE;
    }
    //End edit
    return TRUE;
}
if ( isValidReturnURL( $_GET["returnto"] ) && $continue ) {
    header("Location:" . urldecode( $_GET["returnto"] ) );
}

Me gustaría saber si este código es seguro o si un atacante podría hacer que la página se redirija a un sitio web externo (oa otras ubicaciones maliciosas).

Actualización: Anteriormente, mi código para garantizar que los enlaces fueran locales era el siguiente:

...
if ( $cleanString[0] !== '/' ) { //All local urls should start with '/'
    return FALSE;
}
...

Como se indica en varios comments , esto considerará que las URLS relativas al protocolo (por ejemplo, //google.com) son válidas. Por lo tanto, actualicé el código anterior para tenerlo en cuenta.

    
pregunta Inkbug 24.08.2016 - 18:23
fuente

5 respuestas

1

En lugar de validar si la URL es incorrecta, simplemente antepone la ruta de contexto de su aplicación.

  1. Verifique que $_GET["returnto"] no contenga retornos de línea ( \r o \n ), porque no estoy seguro si la función header está realizando esta verificación de seguridad internamente.
  2. Escriba la ubicación del redireccionamiento con la ruta de contexto prefijada

    header("Location: https://example.com/" . $_GET["returnto"] );
    

    Entonces, si se pasa http://badguy.net , el usuario será redirigido de forma segura a https://example.com/http://badguy.net , que es una página 404 inofensiva proporcionada por su sitio.

  3. Ya no es necesario que verifiques que las URL son relativas.

Nota: vi donde aparentemente cometiste este error anteriormente. Si urldecode cuando actúa en la variable, también debe urldecode cuando desinfecte / verifique si es válida. De manera similar, si no hace urldecode al sanear, no debe urldecode al actuar.

  1. Advertencia: asegúrese de no tener ningún controlador de redireccionamiento abierto. Por ejemplo, algunos sitios utilizan una URL especial como https://example.com/go?url=http://google.com para que cuando un usuario haga clic en el sistema pueda iniciar sesión cuando lo haya hecho.

    Si tiene una característica de este tipo, entonces debe agregar una verificación de seguridad por separado, de lo contrario, el usuario podría pasar go?url=http://badguy.net (que es una URL relativa), acceder al redireccionamiento abierto, y luego el usuario será redirigido a http://badguy.net .

respondido por el George Bailey 24.08.2016 - 20:49
fuente
4

No es seguro:

returnto=//google.com

Esta es una URL relativa al protocolo, se redireccionará a enlace si su sitio es https, de lo contrario, se redireccionará a enlace

    
respondido por el tim 24.08.2016 - 18:27
fuente
3

Entonces, como tengo mejores cosas que hacer, acabo de pasar un tiempo tratando de encontrar un carácter de espacio en blanco que no esté recortado por el recorte, y por lo tanto desordena a parse_url, pero es un espacio en blanco válido en el encabezado función.

Descubrí que si codifico el formfeed de caracteres Unicode, U + 000C, y lo coloco antes de la URL como sigue:

%0Chttp%3A%2F%2Fwww.google.com

Tu código aún se redireccionará a Google. ¡Lo siento! : D

    
respondido por el Joseph Young 24.08.2016 - 20:16
fuente
2

La solución validada es vulnerable (ver otras publicaciones).

Te sugiero que LISTE EN BLANCO las direcciones URL de redireccionamiento. Aún mejor: usar un mapeador para una lista blanca.

&something=value&redirectUrl=3

3 se asigna en el backend a una URL agradable. Al hacer esto estás seguro

Si tiene una URL en la lista blanca, tenga cuidado también con los parámetros ... Por eso, para estar 100% seguro y tener un lugar agradable donde se almacenen todas las URL (lo cual es fácil de mantener), solo permitiría claves específicas (como el "3" en el ejemplo de aquí arriba) que asignaría para limpiar las URL.

El punto de esta técnica es que no tendrás que pensar qué posibles hacks podrían romperte (como viste con la solución validada, podría surgir una nueva idea y romper tus defensas). Si aplica esta técnica, está seguro de que controla la redirección.

El único inconveniente (funcional) es que no se puede redireccionar a una URL dinámica. Lo que probablemente no planeas hacer, dado tu caso de uso :)

La línea inferior es: tener una URL de redireccionamiento como valor de parámetro en una URL es generalmente una mala idea, ya que la validación es propensa a errores. Si puedes evitar la validación por completo, ¡hazlo! Incluso si los estándares evolucionan (ver: URL relativos), todavía estás a salvo.

    
respondido por el niilzon 25.08.2016 - 13:30
fuente
2

La mejor manera de corregir una vulnerabilidad es evitarla por completo.

Considere tokenizar o poner en lista blanca lo que es posible. ¡No use GET / POST para pasar variables sensibles! Considere usar $_SESSION['redirect'] o set_cookie() para pasar este valor. Un atacante no puede establecer una cookie en un dominio remoto a menos que tenga XSS o división de respuesta HTTP. En ese momento, XSS es mucho más valioso que un simple redireccionamiento abierto.

    
respondido por el rook 25.08.2016 - 08:32
fuente

Lea otras preguntas en las etiquetas