Foros del Web » Programando para Internet » PHP »

Sistema de LOGIN definitivo

Estas en el tema de Sistema de LOGIN definitivo en el foro de PHP en Foros del Web. Muy buenas! He creado desde 0 un sistema de LOGIN que me gustaría que revisaran! Creo que es 100% seguro e impenetrable, aunque quizás aún ...
  #1 (permalink)  
Antiguo 31/07/2014, 06:26
Avatar de fbedia  
Fecha de Ingreso: julio-2010
Mensajes: 159
Antigüedad: 13 años, 9 meses
Puntos: 8
Sistema de LOGIN definitivo

Muy buenas!

He creado desde 0 un sistema de LOGIN que me gustaría que revisaran!
Creo que es 100% seguro e impenetrable, aunque quizás aún se pueda mejorar más?? alguna validación que me falte o me sobre??

Quiero que si el usuario realiza 10 intentos de login fallidos... que el sistema le bloquee durante 10 minutos. Para hacer esto, uso una variable de sesion.
No estoy puesto en temas de fuerza bruta... pero ¿quizas un programa de estos pueda realizar un intento de inicio de sesion... despues matar la sesion... y hacer otro? De esta forma se saltaria la comprobacion echa.... :(

Bueno... os dejo el código y quedo a la espera de sus comentarios.
Tengan en cuenta que los echo "---"; son meras trazas...

Saludos!

Código PHP:
Ver original
  1. if(isset($_POST['start'])) {
  2.     // Comprobamos si existe la variable intentos_start
  3.     if(isset($_SESSION['intentos_start'])) {
  4.         if($_SESSION['intentos_start']<10) {
  5.             $_SESSION['intentos_start']=$_SESSION['intentos_start']+1;
  6.         }
  7.     } else {
  8.         $_SESSION['intentos_start']=1;
  9.     }
  10.    
  11.     if($_SESSION['intentos_start']==10) {
  12.         // Establezco el tiempo para proximo intento
  13.         $hora = strtotime('+10 minutes');  
  14.         $_SESSION['time_to_start']=$hora;
  15.         $_SESSION['intentos_start']=$_SESSION['intentos_start']+1;
  16.         echo "<p>HAS ALCANZADO EL MAXIMO DE INTENTOS... ESPERA 10 MINUTOS</p>";
  17.     } else {
  18.         if($_SESSION['intentos_start']>10) {
  19.             // Comprobamos si ya ha esperado el tiempo suficiente
  20.             $hora = strtotime('now');
  21.             if($hora>$_SESSION['time_to_start']) {
  22.                 // Si la hora es mayor, es que ya ha esperado el tiempo suficiente para volver a intenarlo
  23.                 // Al resetear la variable intentos_start entrara en el bucle de CONSULTA A BD
  24.                 $_SESSION['intentos_start']=1;
  25.             } else {
  26.                 // Aun no ha esperado lo suficiente
  27.                 $tiempo_restante_seg = $_SESSION['time_to_start']-$hora;
  28.                 $tiempo_restante_min = $tiempo_restante_seg/60;
  29.                 echo "<p>tienes que esperar todabia ".substr($tiempo_restante_min,0,1)." minutos</p>";
  30.             }
  31.         }
  32.         if($_SESSION['intentos_start']<10) {
  33.             // Realizamos la comprobacion del login
  34.             // 1º Comprobamos la recepcion del usuario y password
  35.             if(isset($_POST['password']) && isset($_POST['username'])) {
  36.                 //2º Escapamos los valores recibidos
  37.                 $username=addslashes($_POST['username']);
  38.                 $password=addslashes($_POST['password']);
  39.                 //3º Comprobamos la longitud que debe estar entre 2 y 25 caracteres el usuario y minimo 4 la password
  40.                 $long_username=strlen($username);
  41.                 $long_password=strlen($password);
  42.                 if(($long_username<2)||($long_username>25)||($long_password<4)||($long_password>25)) {
  43.                     echo "<p>LONGITUD INVALIDA USUARIO Y PASSOWRD</p>";
  44.                 } else {
  45.                     // Comprobamos que solo haya numeros o letras
  46.                     if((preg_match("/^[0-9a-zA-Z]+$/", $username))&&(preg_match("/^[0-9a-zA-Z]+$/", $password))) {
  47.                         // Llegados a este punto, no seria necesario hacer un mysqli_real_escape_string...
  48.                         // pero se lo hare por prevenir:
  49.                         $username=mysqli_real_escape_string($link_db,$username);
  50.                         // Consultamos el usuario en la BD
  51.                         $query_usuario = mysqli_query($link_db,"SELECT password FROM usuarios WHERE nickname='$username'");
  52.                         // Comprobamos que exista el usuario
  53.                         $existe_usuario=mysqli_num_rows($query_usuario);
  54.                         if($existe_usuario==1) {
  55.                             $row1 = mysqli_fetch_array($query_usuario, MYSQLI_ASSOC);
  56.                             // Comprobamos que la password coincida con la recibida del form
  57.                             if($row1['password']==md5($password)) {
  58.                                 // Login success
  59.                                 // Asignamos las variables de sesion... permisos... oportunos:
  60.                                 echo "usuario y pass correctos....";
  61.                             } else {
  62.                                 echo "PASSWORD INCORRECTA";
  63.                             }
  64.                         } else {
  65.                             echo "EL USUARIO NO EXISTE $username";
  66.                         }
  67.                     } else {
  68.                         echo "CARACTERES NO ADMITIDOS";
  69.                     }
  70.                 }
  71.             } else {
  72.                 echo "<p>NO SE RECIBIO USUARIO Y PASSOWRD</p>";
  73.             }
  74.         }
  75.     }
  76. }


y el form:

Código HTML:
Ver original
  1. <form action="procesar" method="post">
  2.                                         <div class="form-group">
  3.                                             <input type="text" class="form-control" id="username" name="username" title="Usuario" placeholder="Usuario" />
  4.                                         </div>
  5.                                         <div class="form-group">
  6.                                             <input type="password" class="form-control" id="password" name="password" title="Password" placeholder="Password" />
  7.                                         </div>
  8.                                         <ul class="list-unstyled pull-left">
  9.                                             <li><a href="#">Recuperar password</a></li>
  10.                                             <li><a href="#">Crear cuenta</a></li>
  11.                                         </ul>
  12.                                         <button type="submit" name="start" class="btn btn-custom pull-right">Start</button>
  13.                                     </form>
__________________
Follow me on twitter @franbedia
  #2 (permalink)  
Antiguo 31/07/2014, 09:38
Avatar de thedaket  
Fecha de Ingreso: junio-2011
Mensajes: 55
Antigüedad: 12 años, 9 meses
Puntos: 3
Respuesta: Sistema de LOGIN definitivo

a mi humilde opinión creo que algunas validaciones bien podrían estar en javascript para no estar escribiendo HTML en PHP.

OJO no digo que no haya una validación de vacíos

Con esto evitarías estar haciendo POST y por ende hacer esperar al usuario solo para saber que no escribió nada

De ahí en fuera creo que esta bien ya que estas limpiando bien las cadenas para evitar SQL injection pero y de ataques XSS jajaja naaaa es broma eso es algo más complicado.

Suerte
__________________
Sí AJAX quieres usar jQuery debes agregar...
  #3 (permalink)  
Antiguo 31/07/2014, 10:26
Colaborador
 
Fecha de Ingreso: mayo-2008
Ubicación: $MX['VZ']['Xalapa']
Mensajes: 3.005
Antigüedad: 15 años, 11 meses
Puntos: 528
Respuesta: Sistema de LOGIN definitivo

Revisas que usuario y contraseña solo contengan números y letras, sin embargo, estás olvidando la seguridad que brinda una contraseña que contenga caracteres especiales, y haces hash con md5, cuando actualmente se recomienda usar blowfish.

En mi opinión (y es sólo eso, una opinión) si usaras PDO con consultas preparadas y hash en blowfish, sería mucho más seguro.

Además, no estaría mal si quieres evitar ataques por fuerza bruta, que revises la ip del usuario, para evitar que de una misma ip intenten entrar con diferentes usuarios.
  #4 (permalink)  
Antiguo 01/08/2014, 02:05
Avatar de fbedia  
Fecha de Ingreso: julio-2010
Mensajes: 159
Antigüedad: 13 años, 9 meses
Puntos: 8
Respuesta: Sistema de LOGIN definitivo

Gracias por los comentarios!

Cita:
a mi humilde opinión creo que algunas validaciones bien podrían estar en javascript para no estar escribiendo HTML en PHP.

OJO no digo que no haya una validación de vacíos

Con esto evitarías estar haciendo POST y por ende hacer esperar al usuario solo para saber que no escribió nada

De ahí en fuera creo que esta bien ya que estas limpiando bien las cadenas para evitar SQL injection pero y de ataques XSS jajaja naaaa es broma eso es algo más complicado.

Suerte
Tienes razón, seguramente lleve el sistema a XAJAX ;)

Cita:
Revisas que usuario y contraseña solo contengan números y letras, sin embargo, estás olvidando la seguridad que brinda una contraseña que contenga caracteres especiales, y haces hash con md5, cuando actualmente se recomienda usar blowfish.

En mi opinión (y es sólo eso, una opinión) si usaras PDO con consultas preparadas y hash en blowfish, sería mucho más seguro.

Además, no estaría mal si quieres evitar ataques por fuerza bruta, que revises la ip del usuario, para evitar que de una misma ip intenten entrar con diferentes usuarios.
Ayer 09:38
Había pensado en lo de la contraseña.... y la verdad que por no complicarme lo deje así! Voy a tratar de incluir también caracteres no alfanuméricos.

Ok también a lo de md5.. Echare un vistazo a la encriptacion que me comentas.
En otras ocasiones he usado una combinación de varias, como md5(sha1("$password"))...

Lo de restringir la IP a un solo usuario no me convence mucho la verdad....
Seria raro que se de el caso, pero hay posibilidades de que 2 o más usuarios conecten desde una misma IP... por ejemplo si estan entrando desde una misma red wiffi, como la biblioteca, centro comercial, empresa...etc
__________________
Follow me on twitter @franbedia
  #5 (permalink)  
Antiguo 01/08/2014, 03:13
Avatar de Eleazan  
Fecha de Ingreso: abril-2008
Ubicación: Ibiza
Mensajes: 1.879
Antigüedad: 16 años
Puntos: 326
Respuesta: Sistema de LOGIN definitivo

Veo un fallo "grave", que te diga si el usuario es erróneo, o la contraseña. El mensaje debería ser el mismo para ambos casos. ¿Pq decirle si ese usuario existe o no?

Otra, el "bloqueo" va por sesión. ¿Y si inicio con otro navegador? ¿Una ventana de incógnito? Podría saltarme el bloqueo, y seguir probando contraseñas hasta dar con ella...

No me parece lo más acertado
__________________
>> Eleazan's Source
>> @Eleazan
  #6 (permalink)  
Antiguo 01/08/2014, 05:23
Avatar de HackID1  
Fecha de Ingreso: febrero-2013
Ubicación: En Update
Mensajes: 492
Antigüedad: 11 años, 2 meses
Puntos: 17
Respuesta: Sistema de LOGIN definitivo

Bueno ya te han comentado igual varias mejoras que había visto en el código, aunque claro poco a poco seguro lo mejoras :) .
-Lo que si veo importante es lo de permitir contraseñas con Caracteres especiales y mostrar el mensaje de error igual tanto cuando se equivoca en usuario o en contraseña.

Saludos y buen login. !!!.
__________________
Puntuar +1 es buena forma de dar las gracias. :P
Your Time is limited, so don't waste it living someone else´s life.
Por: HackID1
  #7 (permalink)  
Antiguo 04/08/2014, 02:14
Avatar de fbedia  
Fecha de Ingreso: julio-2010
Mensajes: 159
Antigüedad: 13 años, 9 meses
Puntos: 8
Respuesta: Sistema de LOGIN definitivo

Hola!

Finalmente añadi un bloqueo por IP.
Al llegar a los 10 intentos, capturo la IP del usuario y la añado a una tabla de bloqueos de IP... (dicha tabla la consulto al inicio de cada conexion)
Simplemente añadi:
Código PHP:
Ver original
  1. $query_bloqueo_ip = mysqli_query($link_db,"
  2.             INSERT INTO black_ips (ip,motivo,id_usuario,timestamp) VALUES (
  3.             '$global_ip_usuario',
  4.             'Has alcanzado el n&uacute;mero m&aacute;ximo de intentos en el inicio de sesi&oacute;n.',
  5.             1,
  6.             $hora
  7.             )");

Cambie tambien la expresion regular para la password, de forma que permita lo siguientes caracteres:
# @ ! - $ & ? _
codigo:
Código PHP:
Ver original
  1. preg_match("/^[0-9a-zA-Z#@!-$&?_]+$/", $password)

Duda: ¿es correcta mi expresion regular?
He realizado alguna prueba y creo que si... pero la verdad que no se me dan muy bien las expresiones regulares y quizas se me escape alguna cosa o se pueda mejorar...

Por ultimo, los mensajes de error distintos para "usuario incorrecto" y "password incorrecto" que me comentáis, estan asi simplemente para las pruebas.... Una vez lo publique dejare el mismo mensaje de "usuario o password incorrectos".

Saludos
__________________
Follow me on twitter @franbedia
  #8 (permalink)  
Antiguo 06/08/2014, 18:13
Colaborador
 
Fecha de Ingreso: mayo-2008
Ubicación: $MX['VZ']['Xalapa']
Mensajes: 3.005
Antigüedad: 15 años, 11 meses
Puntos: 528
Respuesta: Sistema de LOGIN definitivo

Sería bueno que mostraras cómo revisas si la ip ya tiene x intentos, ya que por medio de la ip podrían inyectarte código sql si no filtras el valor de la ip.
  #9 (permalink)  
Antiguo 07/08/2014, 00:55
Avatar de bookmaster  
Fecha de Ingreso: febrero-2002
Ubicación: Toledo
Mensajes: 976
Antigüedad: 22 años, 2 meses
Puntos: 67
Respuesta: Sistema de LOGIN definitivo

Sobre el tema de bloquear las IP's, te puedo decir por experiencia, que te hacen el ataque desde diversas IP's a la vez ya que usan proxys.

He llevado un seguimiento de por donde han tratado de entrar en una de las webs que llevo y generan mas de 10 intentos en menos de 5 segundos al mismo, probé a bloquearlas desde SQL, .htacess, y tal pero es tontería por lo que te acabo de comentar.

La mejor opción es bloquear la cuenta directamente cuando superen dicho número de intentos, y después de un cierto tiempo desbloquearla, por si el usuario real quiere volver a entrar.

Otra opción es controlar la ubicación del que trata de acceder, tipo lo que hace google, si por ejemplo te conectas siempre desde la ubicación X, y un día de buenas a primeras tras diferentes intentos se conecta desde una ubicación Y, bloqueas la cuenta en el momento, cambias la contraseña automáticamente generando una aleatoria y le mandas un mail a la cuenta de correo con la que se registró dicho usuario con un aviso de que su cuenta a sido comprometida y los pasos para modificar la contraseña.
__________________
Decir si te a funcionado la respuesta es ¡GRATIS!. Por favor indicarlo.
http://www.lohacemosweb.net
http://tutoriales.lohacemosweb.net
  #10 (permalink)  
Antiguo 07/08/2014, 02:30
Avatar de fbedia  
Fecha de Ingreso: julio-2010
Mensajes: 159
Antigüedad: 13 años, 9 meses
Puntos: 8
Respuesta: Sistema de LOGIN definitivo

Gracias por las respuestas!

Cita:
Iniciado por ocp001a Ver Mensaje
Sería bueno que mostraras cómo revisas si la ip ya tiene x intentos, ya que por medio de la ip podrían inyectarte código sql si no filtras el valor de la ip.
En primer lugar, la IP la obtengo de la siguiente forma:
Código PHP:
Ver original
  1. if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $global_ip_usuario = $_SERVER['HTTP_CLIENT_IP']; } else {
  2.     if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $global_ip_usuario = $_SERVER['HTTP_X_FORWARDED_FOR']; } else {
  3.     $global_ip_usuario = $_SERVER['REMOTE_ADDR']; } }

La IP la tengo siempre accesible en la variable $global_ip_usuario;

En el código que postee al principio, hay una parte que cuando llegas a los 10 intentos te bloquea la posibilidad de login por sesion.... ahi simplemente he añadido un INSERT INTO a una tabla con Ips bloqueadas:

Código PHP:
Ver original
  1. $hora = strtotime('+10 minutes');
  2.  
  3. $query_bloqueo_ip = mysqli_query($link,"
  4.             INSERT INTO black_ips (ip,motivo,id_usuario,timestamp) VALUES (
  5.             '$global_ip_usuario',
  6.             'Has alcanzado el n&uacute;mero m&aacute;ximo de intentos en el inicio de sesi&oacute;n.',
  7.             1,
  8.             $hora
  9.             )");

Una vez añado la IP a la lista negra, si esta intenta acceder la bloqueo!

Tengo un fichero "seguridad.php" que esta a modo de include(); en la primera linea de todos mis ficheros... Este tiene una parte al principio del todo donde hago lo siguiente:

Código PHP:
Ver original
  1. // compruebo lista negra de IPs antes de continuar con los links a las BD
  2.     // primero saco la IP del usuario
  3.     if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $global_ip_usuario = $_SERVER['HTTP_CLIENT_IP']; } else {
  4.     if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $global_ip_usuario = $_SERVER['HTTP_X_FORWARDED_FOR']; } else {
  5.     $global_ip_usuario = $_SERVER['REMOTE_ADDR']; } }
  6.    
  7.     $hora_actual = strtotime('now');
  8.     $result = mysqli_query($link, "SELECT black_ips.motivo,usuarios.nickname,black_ips.timestamp  
  9.     FROM black_ips, usuarios
  10.     WHERE black_ips.ip='$global_ip_usuario'
  11.     AND black_ips.id_usuario=usuarios.id_usuario
  12.     AND black_ips.timestamp>$hora_actual");
  13.     $contador_result = mysqli_num_rows($result);
  14.     if($contador_result>=1) {
  15.         $global_errores = 1;
  16.         $global_error_txt = "<p>SU DIRECCI&Oacute;N IP <b>$global_ip_usuario</b> SE ENCUENTRA EN LA <b>LISTA NEGRA</b>:</p>";
  17.         while($row=mysqli_fetch_array($result)) {
  18.             $global_error_txt .= " - <b><span style='color:red'>@".$row[1]."</span></b> a&ntilde;adio el siguiente motivo:
  19.                                 <b><i>".$row[0]."</i></b><br/>";
  20.             $horas_restantes_bloqueo = (($row[2]-$hora_actual)/60)/60;
  21.             $global_error_txt .= "[$horas_restantes_bloqueo horas restantes para la eliminaci&oacute;n de este bloqueo]<br/><br/>";
  22.         }
  23.     } else {
  24.         //este 'else' indica que no se encuentra en la lista negra de IPs... asi que continuamos:


Cita:
Iniciado por bookmaster Ver Mensaje
Sobre el tema de bloquear las IP's, te puedo decir por experiencia, que te hacen el ataque desde diversas IP's a la vez ya que usan proxys.

He llevado un seguimiento de por donde han tratado de entrar en una de las webs que llevo y generan mas de 10 intentos en menos de 5 segundos al mismo, probé a bloquearlas desde SQL, .htacess, y tal pero es tontería por lo que te acabo de comentar.

La mejor opción es bloquear la cuenta directamente cuando superen dicho número de intentos, y después de un cierto tiempo desbloquearla, por si el usuario real quiere volver a entrar.

Otra opción es controlar la ubicación del que trata de acceder, tipo lo que hace google, si por ejemplo te conectas siempre desde la ubicación X, y un día de buenas a primeras tras diferentes intentos se conecta desde una ubicación Y, bloqueas la cuenta en el momento, cambias la contraseña automáticamente generando una aleatoria y le mandas un mail a la cuenta de correo con la que se registró dicho usuario con un aviso de que su cuenta a sido comprometida y los pasos para modificar la contraseña.
Gracias! Creo que si que es buena idea lo que comentas.
Actualmente tengo una tabla donde almaceno las ultimas 10 IPs con las que ha accedido un usuario... y el propio usuario lo puede ver desde su perfil.

Aun tengo que dar una vuelta a tu comentario para ver como lo hago, pero lo primero que se me viene a la cabeza es crear una nueva columna que ponga "intentos_login_fallidos" y que sume 1 cada vez que falla el login....
Si este valor llega a 10: bloquear la cuenta, y enviar email al usuario para desbloquearla mediante la generacion de nueva password.
Tambien hacer que si son 5 intentos fallidos seguidos... bloquear BIS a lo anterior.

Un saludo.
__________________
Follow me on twitter @franbedia

Última edición por fbedia; 07/08/2014 a las 02:32 Razón: Errata
  #11 (permalink)  
Antiguo 07/08/2014, 14:52
Colaborador
 
Fecha de Ingreso: mayo-2008
Ubicación: $MX['VZ']['Xalapa']
Mensajes: 3.005
Antigüedad: 15 años, 11 meses
Puntos: 528
Respuesta: Sistema de LOGIN definitivo

$query_bloqueo_ip = mysqli_query($link,"
INSERT INTO black_ips (ip,motivo,id_usuario,timestamp) VALUES (
'$global_ip_usuario',

Aquí, por experiencia te digo, pueden acceder indicando ya no una ip falsa, si no una ip imposible, por ejemplo ' or 1=1, es decir, si no filtras dicha ip, podrían estar inyectando código
  #12 (permalink)  
Antiguo 08/08/2014, 01:32
Avatar de fbedia  
Fecha de Ingreso: julio-2010
Mensajes: 159
Antigüedad: 13 años, 9 meses
Puntos: 8
Respuesta: Sistema de LOGIN definitivo

Ten en cuenta que la IP la obtengo usando $_SERVER[...]
¿En serio es posible modificar tu IP por un "string" cualquiera? (Interesante.....)
Buscare por google aunque si tienes más info al respecto agradecería me la enviases!

Por si acaso usare una expresión regular para validar la IP. No esta de más.

Gracias!
__________________
Follow me on twitter @franbedia
  #13 (permalink)  
Antiguo 09/08/2014, 04:58
 
Fecha de Ingreso: febrero-2014
Mensajes: 46
Antigüedad: 10 años, 2 meses
Puntos: 1
Respuesta: Sistema de LOGIN definitivo

Te recomiendo que después de mostrar "Contraseña incorrecta", etc utilices exit(); para que el programa no se siga ejecutando.

Etiquetas: login, mysql, select, sistema, sql, usuarios, variable
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 19:48.