Como limitar los intentos de inicio de sesión con PHP
Hoy en día con tantas facilidades para realizar ataques de fuerza bruta, y el hasta cierto punto mal hábito de usar contraseñas débiles o repetirlas entre cuentas, al momento de desarrollar sistemas uno se ve en la necesidad de incluir un tipo de limites ante estos ataques, una de las formas mas sencillas de realizar esto es denegar el acceso cada vez que se equivoquen X numero de veces, o quizás incluir un captcha o prueba anti-bot tal como es el caso de Google Accounts.
Primero, mostraremos el inicio de sesión que usaremos para este ejemplo (Sin acceso a bases de datos):
Código Web
<?php $usuario = "usuario"; $password = "123456"; $mensaje = "Inicio de Sesión ($usuario & $password)"; if($_POST["enviar"]){ if(!$_POST["user"] || !$_POST["pass"]){ $mensaje = "Complete todos los campos"; } if(($_POST["user"] != $usuario) || ($_POST["pass"] != $password)) { $mensaje = "Datos incorrectos"; }else{ $mensaje = "Datos correctos"; //Aqui seria donde ponemos una cookie, redirigimos, etc. } }else{ } ?> <html> <head> <title>Inicio de sesión</title> </head> <body> <h1><?=$mensaje?></h1><br /> <form method="post" action="login.php"> <input type="text" name="user" value="" /> <br /> <input type="password" name="pass" value="" /> <input type="submit" value="enviar" name="enviar" /> </form> </body> </html>
En este caso no sacaremos los usuarios de una base de datos para ahorrar recursos innecesarios para dicha prueba, ahora bien, si tendremos que usar una conexión a la base de datos para establecer cuantos intentos fallido ha tenido cada IP, esto también se podría realizar mediante una cookie pero la mayoría de los bots no aceptan cookies, por lo que el conteo tendrá que ser desde el lado del servidor valiéndose solamente del IP de la visita.
Por orden lógico lo primero que hay que hacer es crear la tabla en donde tendremos en los campos la fecha del ultimo login (time), si es que se planea levantar el veto después de cierto tiempo, la ip a confinar (ip) y el numero de intentos (intentos);
Ahora bien, lo que sigue seria buscar si la IP ya ha superado el limite de intentos y si es así terminar el proceso de forma abrupta, o al menos en este caso, para realizar eso agregaré las siguientes lineas al principio de nuestro ejemplo;
Código Web
//Bases de datos $conf_mysql_servidor= "localhost"; //<-- editar $conf_mysql_puerto="xxxx"; //<-- editar $conf_mysql_usuario= "usuario"; //<-- editar $conf_mysql_password= "password"; //<-- editar $conf_mysql_db= $conf_mysql_usuario; //<-- editar $conexion = new mysqli ( $conf_mysql_servidor,$conf_mysql_usuario,$conf_mysql_password,$conf_mysql_db,$conf_mysql_puerto); //Buscamos si la IP ya supero el limite de intentos $ip = $_SERVER['REMOTE_ADDR']; $query = "SELECT * FROM `limit-login` WHERE `ip` = '$ip' AND`intentos` = 3"; $query = $conexion ->query($query); $limitlogin = $query ->num_rows; if($limitlogin != 0){ die("<h1>Numero de intentos superados</h1>"); }
Ahora lo que sigue es aumentar un el contador cada vez que el usuario escriba mal los datos de inicio de sesión, lo cual se puede realizar agregando el siguiente código después de indicar que los datos son incorrectos.
Código Web
//Agregamos un +1 si los datos escritos son incorrectos. $query = "SELECT * FROM `limit-login` WHERE `ip` = '$ip'"; $query = $conexion ->query($query); $query1 = $query ->num_rows; if($query1 != 0){ //En caso de ya estar en la BD, sacamos el valor y agregamos +1 $valor = $query ->fetch_assoc(); $contador = $valor['intentos'] + 1; $intentos = "UPDATE `limit-login` SET `intentos` = '$contador' WHERE ip = '$ip';"; }else{ //Pero si es su primer intento fallido, tenemos que incluirlo en la BD $intentos = "INSERT INTO `limit-login` (`time` ,`ip` ,`intentos` )VALUES (CURRENT_TIMESTAMP , '$ip', '1');"; } $conexion -> query($intentos);
Con esto ahora, después de 3 intentos fallidos (o los especificados) se manda a terminar la ejecucion del script, evitando así que continúen intentando iniciar sesión.
Lo unico que faltaría ahora sería mediante un trabajo programado de CRON vaciar la lista de IP’s bloqueadas que hayan cumplido una condena de cierto tiempo (En este caso 24hrs después del último intento), para ello usaremos un script en PHP en el cual indicaremos mediante la diferencia entre el timestamp y el tiempo local si es que se conserva o si es que se borran la lista de IP’s vetadas:
Código Web
<?php //Bases de datos $conf_mysql_servidor= "localhost"; //<-- editar $conf_mysql_puerto="xxxx"; //<-- editar $conf_mysql_usuario= "usuario"; //<-- editar $conf_mysql_password= "password"; //<-- editar $conf_mysql_db= $conf_mysql_usuario; //<-- editar $conexion = new mysqli ( $conf_mysql_servidor,$conf_mysql_usuario,$conf_mysql_password,$conf_mysql_db,$conf_mysql_puerto); $resultado= $conexion ->query("SELECT * FROM `limit-login` WHERE `intentos` = '3';"); while ($table = $resultado ->fetch_assoc()) { //El truco esta en pasar el timestamp a formato UNIX, para sacar al diferencia de segundos. $resta = time() - strtotime($table['time']); if($resta < 86400){ $ip = $table['ip']; $conexion ->query("DELETE FROM `limit-login` WHERE ip = '$ip'"); } } ?>
Ahora agregamos esto a CRON usando ya sea, si es que esta disponible, PHP-CLI o en su defecto un explorador web de linea de comando como lynx o incluso con curl para realizar la petición en este caso cada dia (@daily)
Usando PHP-CLI
Código Web
@daily php /home/ruta/a/cron.php
Usando Lynx
Código Web
@daily lynx -dump "http://www.tudominio.algo/cron.php" >/dev/null 2>&1
Usando CURL
Código Web
@daily curl http://www.tudominio.algo/cron.php
Recomiendo usar siempre PHP-CLI y usar un directorio que no esté disponible via WEB, pero si se tiene que hacer de esa forma, usar un directorio protegido y CURL para autentificarse.
No hay comentarios.