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?
- 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.
- 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?
- Posible ataque de inyección SQL en
DB.lookup(user)
, pero también es posible que DB.lookup()
maneje esto.
-
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
.
- 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.