tag - Prevención de fuerza bruta/DoS en PHP
strip_tags() (5)
Estoy intentando escribir un script para evitar intentos de inicio de sesión de fuerza bruta en un sitio web que estoy construyendo. La lógica es algo como esto:
- El usuario envía información de inicio de sesión.
- Compruebe si el nombre de usuario y la contraseña son correctos
- Si es así, déjalos entrar.
- Si No, registre un intento fallido en la base de datos. Compruebe si hay demasiados errores dentro de un período de tiempo determinado (por ejemplo, 5 en 5 minutos):
- Si la respuesta es Sí, entonces haga una pausa en la ejecución durante 10 segundos:
sleep(10)
, luego reporte un error de inicio de sesión al usuario. - Informar de un error de inicio de sesión al usuario inmediatamente
- Si la respuesta es Sí, entonces haga una pausa en la ejecución durante 10 segundos:
Al explicarle esto a un compañero de trabajo, me preguntaron cómo ayudaría esto si un pirata informático enviara, por ejemplo, 1000 solicitudes en un segundo. ¿Los primeros 5 regresarían inmediatamente, y luego los 995 restantes tomarán solo 10 segundos?
Tengo la sospecha de que no entiendo completamente cómo funciona HTTP. ¿Es posible incluso esa situación anterior o existe un límite en el número de solicitudes simultáneas que un servidor manejará desde un cliente?
¿Sería una mejor solución tener un mayor tiempo de sueño?
sleep($numRequestsInLast5Minutes - 5)
Así que los primeros 5 serían rápidos, y luego cada uno subsiguiente aumentaría el sueño.
El problema es el equilibrio entre la accesibilidad del usuario y el modelo de atacante.
Primera solucion
If not password correct for a certain number of time:
block the user
send a reset link to the user
Usuario : podría ser bloqueado, y no les gusta reiniciar
Atacante : bloqueó a todos los usuarios al intentar autenticarse ante todos los usuarios (especialmente si todos los inicios de sesión están disponibles públicamente)
Segunda solucion
If not password correct:
sleep(amount_of_time)
La pregunta es: ¿cuál es el valor de ''amount_of_time''?
Usuario : puede ser molesto esperar ''amount_of_time'' por cada error
Atacante : sigue intentándolo, con menor prueba / segundos.
Tercera solucion
If not password correct:
sleep(amount_of_time)
amount_of_time = amount_of_time * 2
Usuario : menos molesto por pocos errores de contraseña
Atacante : bloquea al usuario para conectarse enviando mucha contraseña incorrecta
Cuarta Solución
If not password correct for a certain number of time:
submit a catchpa
Usuario : necesita resolver el CAPTCHA (no demasiado complejo)
Atacante : necesidad de resolver el CAPTCHA (debe ser complejo)
Buena solución (y utilizada por muchos sitios) pero tenga cuidado con nuestro CAPTCHA. implementacion De todos modos hay un truco (ver siguiente solución).
Quinta solucion
If not password correct for a certain number of time:
block the ip
(eventually) send a reset link
Usuario : El usuario puede estar bloqueado porque no puede recordar correctamente su contraseña.
Atacante : intentar la misma contraseña con un usuario diferente, porque el bloqueo se basa en el número de inicio de sesión por usuario.
Solución final ?
If several login attempts failed whatever is the user by an IP :
print a CAPTCHA for this IP
Usuario : El usuario no puede estar bloqueado por IP, pero debe recordar su contraseña.
Atacante : difícil tener un ataque eficiente de fuerza bruta.
Las notas importantes
¿El formulario de inicio de sesión o el enlace de inicio de sesión que está bloqueado? Bloquear el formulario de inicio de sesión es inútil.
La resistencia a la fuerza bruta es PRIMERO un problema de complejidad de contraseña, por lo que necesita una política de contraseña estricta (especialmente en el caso de fuerza bruta distribuida).
No menciono el hecho de hash tus contraseñas con Salt, ¿ya estás haciendo esto bien? Porque si es más fácil acceder a la base de datos de contraseñas que a la fuerza bruta, el atacante elegirá esta solución ( "Una cadena es tan fuerte como su eslabón más débil" ).
He usado algo como esto ...
Ver nombre de usuario y contraseña
1.1 Si no coincide, registre el último tiempo de inicio de sesión fallido para ese combo y la cantidad de inicios de sesión fallidos.
1.2 Cada falla hace la espera entre poder iniciar sesión algo así como failCount * 30 segundos, hasta un máximo (por ejemplo, 10 minutos).
- Esto significa que un ataque de fuerza bruta tomará exponencialmente más y más tiempo.
- Puede bloquear a un usuario, pero no contará un inicio de sesión fallido al intentar iniciar sesión durante el período de bloqueo. Esto debería minimizarlo.
He desarrollado esto pero aún no lo he lanzado a la naturaleza, por lo que cualquier comentario será apreciado.
Hice una clase que se ocupa de la protección contra ataques de fuerza bruta en PHP.
https://github.com/ejfrancis/BruteForceBlocker
registra todos los inicios de sesión fallidos en todo el sitio en una tabla db, y si el número de inicios de sesión fallidos en los últimos 10 minutos (o cualquier período de tiempo que elija) supera un límite establecido, impone un retraso de tiempo y / o un requisito de captcha antes de iniciar sesión de nuevo.
ejemplo:
// construir la matriz de ajustes del acelerador. (# recientes inicios de sesión fallidos => respuesta).
$ throttle_settings = [
50 => 2, //delay in seconds 150 => 4, //delay in seconds 300 => ''captcha'' //captcha
];
$ BFBresponse = BruteForceBlocker :: getLoginStatus ($ throttle_settings);
// $ throttle_settings es un parámetro opcional. si no está incluido, se usará la matriz de configuración predeterminada en BruteForceBlocker.php
interruptor ($ BFBresponse [''status'']) {
case ''safe'': //safe to login break; case ''error'': //error occured. get message $error_message = $BFBresponse[''message'']; break; case ''delay'': //time delay required before next login $remaining_delay_in_seconds = $BFBresponse[''message'']; break; case ''captcha'': //captcha required break;
}
Le sugeriría que si el usuario lo intentó sin éxito, digamos más de cinco veces y cinco minutos, comience a devolver inmediatamente un 503 Service Unavailable
para esa dirección IP. Cuando falla el inicio de sesión, puede utilizar memcache para obtener los intentos incorrectos actuales de una IP, y luego incrementar la cantidad y guardarla en memcache con un vencimiento de 5 minutos.
No querrá poner un estado de sleep
en su código PHP, ya que eso permitirá que un solo usuario cree muchas conexiones a su servidor web y, posiblemente, desactive a otros usuarios.
Dado que el usuario no ha iniciado sesión, no tiene una cookie de sesión, y si el usuario está tratando de ingresar en una cuenta, es posible que no presente ninguna cookie.
No estoy seguro de cuál es la mejor práctica, pero cuando se trata de ataques DoS, una mejor estrategia es desviar el tráfico de su servidor. La configuración de los tiempos de espera en realidad no ayudará porque todavía está procesando la solicitud y ejecutando PHP.
¿Ha considerado configurar otro servidor web que ejecute una versión simplificada más simple de su página de inicio de sesión? Cuando el usuario lo intenta demasiadas veces (por ejemplo, miles de veces), envíe un mensaje para configurar su enrutador y redirija a este usuario al segundo servidor web.
Es como cuando los sitios web se ven afectados por el efecto slashdot, muchos de ellos simplemente redirigen el tráfico hasta que se reduce el tráfico.