¿Perdí alguna vulnerabilidad aquí?

5

Recientemente recibí una paliza en una entrevista con el ingeniero de seguridad y no recibí ningún comentario específico sobre cómo lo hice. Me gustaría volver a hacer la pregunta a la comunidad para poder aclarar estos elementos. Mi lista de vulnerabilidades encontradas se encuentran a continuación.

Está en una página de inicio de sesión, dos elementos de formulario Usuario: / Contraseña: su nombre de usuario es Peter.

Tras el inicio de sesión exitoso de la aplicación, se le dirige a una página que dice Hola 'Peter'. (La fuente de 'Peter' en esta página proviene de la variable de usuario rellenada en el formulario de nombre de usuario de inicio de sesión)

El código genérico para esto es el siguiente:

public String login( String user, String pass ) { 
       if (pass == DB.lookup(user)){ 
            return "Hello " + user; 
           }
       else { .....} 
       } 
  1. ¿Cuáles son todas las vulnerabilidades presentes en esta aplicación de inicio de sesión de muestra?
  2. ¿Cómo los arreglarías?
  3. ¿Cómo almacenarías tus contraseñas?
  4. Si los almacena de esa manera, ¿mediante qué mecanismo recupera la contraseña para validarla contra lo que el usuario escribió en el formulario de contraseña?

    Esto es lo que veo hasta ahora:

XSS: el hecho de que la entrada del usuario (usuario) se inserta en la página jsp que responde sin pasar por una biblioteca de codificación o validador significa que usted es el propietario.

Solución: Implementaría el desinfectante del proyecto del codificador java OWASP en el elemento HTML.

Almacenaría las contraseñas como un hash con sal usando bcrypt. Una sal única para cada contraseña hash.

    
pregunta Taken Beatings 10.11.2015 - 23:19
fuente

3 respuestas

3

Nadie ha mencionado explícitamente que existe un potencial para la inyección de SQL. La cadena de usuario necesita una validación adecuada para garantizar su presencia y solo contiene un nombre de usuario válido.

    
respondido por el stiabhan 12.11.2015 - 11:39
fuente
3

Sin ver el código en el servidor, no puedo estar 100% seguro. Sobre todo porque no sé lo que DB.lookup() hace. Por lo que sé, podría manejar muchos de estos problemas de seguridad.

public String login( String user, String pass ) { 
   if (pass == DB.lookup(user)){ 
        return "Hello " + user; 
       }
   else { .....} 
   } 
  

¿Cuáles son todas las vulnerabilidades presentes en esta aplicación de inicio de sesión de muestra?   ¿Cómo los arreglarías?

  1. No hay tiempo de espera para intentar iniciar sesión por IP / cookie, y ninguna por usuario. Permite fácilmente un ataque de denegación de servicio (incluso DDoS), y un ataque de fuerza bruta.
  2. No Verificar si user o pass son nulos y, por lo tanto, tienen la longitud adecuada y el formato correcto para verificarlos en la base de datos. ¿Qué sucede si la página devuelve un error y le permite probar y descubrir más a través de prueba y error?
  3. Posible ataque de inyección SQL en DB.lookup(user) , pero también es posible que DB.lookup() maneje esto.
  4. return "Hello " + user; Nuevamente, debido a que no hay una comprobación adecuada, puede usar esto para inject cualquier cosa en la página del servidor. Esto significa que puede ejecutar el código rogue ASP / JSP / PHP / whatever .
  5. Nuevamente, sin ver el código DB.lookup() , no puedo decir qué hace, pero puedo hacer una suposición. Parece que estás comparando pass con el username. . Esto podría significar que puedes intentar iniciar sesión con la contraseña igual a la del nombre de usuario y usarla para iniciar sesión, sin tener su contraseña. Podría iniciar sesión con login("Jake", "Jake"); , y funcionaría.
  

¿Cómo almacenarías tus contraseñas?

En la base de datos, no hay nada a lo que el usuario deba tener acceso, usando bcrypt / hashed + salted (único, obviamente) + una contraseña confiable SlowEquals() . Un SlowEquals / XOR implementado correctamente evitará los ataques de sincronización de canal lateral.

  

Si los almacena de esa manera, ¿mediante qué mecanismo recupera la contraseña para validarla contra lo que el usuario escribió en el formulario de contraseña?

Compruebe si la entrada de la contraseña no es nula, tiene la longitud adecuada para una contraseña y no contiene caracteres no permitidos. Tres intentos máximo. Comprueba si el hash salado corresponde a la contraseña.

Con respecto a user y pass , debe verificar si no son nulos, tienen la longitud adecuada para cada campo en la base de datos y si están en el formato adecuado. Para verificar el formato adecuado, puede usar expresiones regulares para asegurarse de que sea alfanumérico con los símbolos correctos. O puedes eliminar todos los caracteres no utilizados.

Y no lo olvides, debes probar tanto el nombre de usuario como la contraseña con DB.lookup() . De lo contrario, puedes iniciar sesión con el nombre de usuario del usuario como contraseña. ¡Horrible!

El código más seguro podría tener este aspecto (pseudo código no probado, por cierto) :

Pseudo clase para DB:

public static class DB
{
     public static boolean lookup(string u, string p)
     {
          // Test salted hash against the user's hash for that particular username.
          return (encryption.testPassword(p, getUsersHashedPassWord(u))) ? true : false;
     }
}

El pseudocódigo de comprobación de errores:

private boolean properLength(String u, String p)
{
     return ((u.length > 3 && u.length <= 12) && (p.length > 8 && p.length <= 30)) ? true : false;
}

private boolean properFormat(String u, String p)
{
    return (regex.Valid(u, usernameRegex) && regex.Valid(p, passwordRegex)) ? true : false; 
}

private String stripBadStuff(String stuff)
{
    // Just in case or something...
    return EncodingFunction.ToASCII(stuff).regexReplace(badCharacaterRegex, ""); 
}



public String login( String user, String pass ) 
{ 
   if (!loginTriesExceeded) // Currently unhandled for example.
   {
       if ((user != null && pass != null) && properLength(user, pass) && properFormat(user, pass))
       {
            // Unhandled for example. This is so they can't change their IP address to continue trying.
            if (!userLoginTriesExceeded)
            {
               String newUser = stripBadStuff(user);
               String newPass = stripBadStuff(pass);
               // Would be changing DB.lookup() to return true if valid login is detected. DB.lookup(newUser,newPass) will now be assumed that it tests the plaintext password against the salted hash. 
               if (DB.lookup(newUser, newPass)) 
               { 
                    // Prevent injection. This will assume DB.lookup() tests the password against the stored hash.
                    return "Hello " + newUser; 
               }
               else 
               {
                    return "Invalid login.";
               }
           }
           else
           {
                return "User login attempts exceeded.";
           }
        }
        else
        {
             return "Invalid login";
        }
    }
    else
    {
        // Do nothing, or inform the user that they've exceeded max logins.
        return null; // So all code paths return a value. 
    }
} 

Recuerda, eso es un pseudo código no probado. Puede haber algunas cosas que no haya visto.

    
respondido por el Mark Buffalo 12.11.2015 - 15:10
fuente
0

Vulnerabilidades que puedo ver:

  1. Aquí puede que no cuente como una vulnerabilidad (como tal), pero la contraseña no parece estar oculta en ese código.
  2. La comparación de cadenas con == es vulnerable al análisis estadístico de temporización.
  3. El XSS que encontraste.
  4. Supondré que DB.lookup es seguro de usar así sin obtener vulnerabilidades de inyección de SQL. Aún falta el manejo de errores, como lo señalara @SourLolita.
  5. No parece haber ninguna protección XSRF, al menos no es visible en ese código. No sé cómo explotar eso en este caso.

Cómo solucionarlos:

  1. Hash contraseñas usando un algoritmo de hash de contraseña, como bcrypt o scrypt.
  2. Utilice la función de comparación / comprobación de la biblioteca de hashing de contraseñas.
  3. Pase el nombre a través de una función de escape apropiada que, con suerte, proporcione el marco en uso.
  4. Agregar una comprobación de error. Si los nombres de usuario son secretos, la inmunidad de tiempo también se aplica aquí, y entonces será complicado.
  5. Tendría que leer sobre eso. Ha sido un tiempo. Una forma es tener un token aleatorio especial enviado con el formulario y como una cookie y solicitar que coincidan.
respondido por el Simon Lindgren 12.11.2015 - 12:17
fuente

Lea otras preguntas en las etiquetas