Autenticar usuario y guardar en una cookie con PHP

  • Por
Sistema de autenticación de usuarios en PHP, donde se ofrece la opción de memorizar su usuario en el ordenador y guardar una cookie para recordar el usuario y no tener que volverse a autenticar.
Vamos a crear un sistema para autenticar usuarios con PHP, con la particularidad que este sistema va a ofrecer al visitante la opción de guardar su usuario, para que la página lo recuerde en sucesivos accesos y no tenga que volver a autenticarse. El usuario se guardará en una cookie para que el navegador pueda recordarlo en sus distintas visitas.

Esta es una opción muy útil para que el visitante no tenga que estar todo el tiempo autenticándose, con su usuario y contraseña, cada vez que accede a la página web. Seguro que es una opción que habremos visto en un montón de sitios web.

Este artículo hace continuación de una serie de talleres y ejemplos que hemos visto anteriormente en el manual de Autentificación de usuarios en PHP. En este taller no vamos a realizar una autenticación muy elaborada, sino una muy simple, para facilitar el desarrollo. Luego la complejidad la aportará la parte de guardar el usuario en una cookie, que no es difícil de hacer, pero sí requiere de nuevos conocimientos que aplicar.

Explicación de almacenar el usuario en una cookie

Primero vamos a explicar con palabras el modelo de trabajo que vamos a aplicar para almacenar el usuario en una cookie. No he investigado cómo lo podrán hacer esto otras personas en otros desarrollos, pero creo que he ingeniado una forma adecuada para hacerlo.

Digo esto porque lo primero que se me ocurrió fue meter el nombre de usuario y la clave en unas cookies. Sería simplemente crear un par de cookies en el sistema del usuario con esas dos variables. Pero luego pensando, no me parecía muy atractiva la posibilidad de incluir esa información sensible en unas cookies en el ordenador del usuario, por su hubiera alguna persona que pudiera leerlas, copiarlas y utilizarlas en otro ordenador. Igual no me debería preocupar por ello, pero en cualquier caso no me gustaba la posibilidad de almacenar el nombre de usuario y contraseña, sino almacenar otro tipo de información menos crítica.

Entonces lo que se me ocurrió es almacenar el identificador del usuario. Pero claro, si alguien conseguía crear una cookie en el sistema con cualquier identificador de usuario, podría acceder a la cuenta de ese usuario. Así que había que aplicar otra estrategia adicional para asegurar que ese identificador de usuario no se pueda reproducir. Finalmente, decidí generar un número aleatorio cuando el usuario se conecta a la página y se autentica correctamente, y almacenarlo en dos sitios, primero en el registro del usuario en la base de datos y luego en la cookie.

Así, cuando el visitante decide que quiere que el sitio web le recuerde su cuenta de usuario, para no tener que volver a autenticarse en siguientes accesos, se guardan dos cosas en las cookies del navegador: su identificador de usuario y una marca aleatoria (dicha marca también se almacena en la base de datos, asociada a su usuario). En siguientes accesos primero se comprueba si existen esas cookies en el navegador del visitante y si el conjunto de identificador de usuario y la marca aleatoria almacenados en la cookie coincide con lo que tenemos en la base de datos.

Dicho de otra manera, en la cookie guardamos el identificador de usuario. Además, generamos un número aleatorio que guardamos en dos sitios: 1) en la tabla de usuario, en el registro correspondiente al usuario que desea que se le recuerde la clave y 2) en una cookie. Luego cuando el usuario se conecta de nuevo, no sólo se comprueba que tenga la cookie con el identificador de usuario, sino que tenga la otra cookie con la marca aleatoria y que sea la misma que tenemos en la base de datos para ese mismo usuario.

Espero que el sistema se pueda entender. No obstante, espero clarificarlo aun más a medida que explique el código PHP y la base de datos que vamos a utilizar.

Tabla de usuario

En la tabla de usuario que hemos creado para este ejemplo tenemos 4 campos:
  • Identificador de usuario
  • Nombre de usuario
  • Clave de acceso
  • Marca aleatoria que se ha metido en la cookie
Para el ejemplo hemos creado la tabla e insertado un par de usuarios para hacer pruebas. Tendrá una forma como esta:


Código de la página PHP

Ahora voy a explicar por partes el código PHP para autenticar al usuario y guardar la información de acceso en la cookie, así como la parte de comprobar si el usuario tenía la cookie con su acceso guardado en el ordenador.

Primero vamos a empezar mostrando el formulario HTML:

<form action="prueba-cookies.php" method="post">
Usuario: <input type="text" name="usuario">
<br>
Clave: <input type="text" name="clave">
<br>
<input type="checkbox" name="guardar_clave" value="1"> Memorizar el usuario en este ordenador
<br>
<input type="submit" value="Entrar">
</form>


Como vemos, tiene los campos para escribir el nombre de usuario y la clave y un campo checkbox adicional para que el usuario marque si quiere que su acceso se guarde en su ordenador.

Ahora voy a mostrar el código que utilizaríamos para recibir por post, del formulario de autentificación, el nombre de usuario y contraseña. Este código también tiene que detectar si el usuario quería que se guardase su acceso en el ordenador.

//debería comprobar si el usuario es correcto
$ssql = "select * from usuario where usuario = '" . $_POST["usuario"] . "' and clave='" . $_POST["clave"] . "'";
//echo $ssql;
$rs = mysql_query($ssql);
if (mysql_num_rows($rs)==1){
   //TODO CORRECTO!! He detectado un usuario
   $usuario_encontrado = mysql_fetch_object($rs);
   //ahora debo de ver si el usuario quería memorizar su cuenta en este ordenador
   if ($_POST["guardar_clave"]=="1"){
      //es que pidió memorizar el usuario
      //1) creo una marca aleatoria en el registro de este usuario
      //alimentamos el generador de aleatorios
      mt_srand (time());
      //generamos un número aleatorio
      $numero_aleatorio = mt_rand(1000000,999999999);
      //2) meto la marca aleatoria en la tabla de usuario
      $ssql = "update usuario set cookie='$numero_aleatorio' where id_usuario=" . $usuario_encontrado->id_usuario;
      mysql_query($ssql);
      //3) ahora meto una cookie en el ordenador del usuario con el identificador del usuario y la cookie aleatoria
      setcookie("id_usuario_dw", $usuario_encontrado->id_usuario , time()+(60*60*24*365));
      setcookie("marca_aleatoria_usuario_dw", $numero_aleatorio, time()+(60*60*24*365));
   }
   echo "Autenticado correctamente";
   //header ("Location: contenidos_protegidos_cookie.php");
   
}else{
   echo "Fallo de autenticación!";
   echo "<p><a href='prueba-cookies.php'>Volver</a>";
}


Para comprobar si los datos de autenticación que recibimos por el formulario son correctos, hacemos una sentencia SQL. La ejecutamos y si nos da como resultado que tenemos un registro encontrado en la tabla de usuarios, es que el nombre de usuario y clave corresponden con el de algún usuario.

Luego con la línea

if ($_POST["guardar_clave"]=="1"){

Comprobamos si el visitante había pedido que se almacenase la clave en su ordenador. Entonces hay que generar las cookies correspondientes, que habíamos comentado anteriormente en este artículo.

Lo hacemos en tres pasos:

  • Genero un número aleatorio para que nos sirva de marca.
  • Inserto la marca aleatoria en la tabla de usuarios, haciendo un update en el registro del usuario autenticado que habíamos detectado anteriormente.
  • Genero y coloco en el ordenador del usuario las dos cookies para guardar su acceso en el navegador: el identificador del usuario y la marca aleatoria. Hemos creado las cookies para que se almacenen durante un año en el ordenador del usuario.

    Nota: Es importante señalar que, para colocar o crer cookies en el navegador del visitante, debemos hacerlo antes de que se hayan enviado las cabeceras de http, es decir, antes de haber escrito cualquier texto en la página. Si no, nos podría dar un error de http headers already sent.


    Por último veamos el código PHP para ver si detectamos las cookies en el navegador de un usuario autenticado anteriormente en el sistema y guardado en el ordenador del usuario.

    //primero tengo que ver si el usuario está memorizado en una cookie
    if (isset($_COOKIE["id_usuario_dw"]) && isset($_COOKIE["marca_aleatoria_usuario_dw"])){
       //Tengo cookies memorizadas
       //además voy a comprobar que esas variables no estén vacías
       if ($_COOKIE["id_usuario_dw"]!="" || $_COOKIE["marca_aleatoria_usuario_dw"]!=""){
          //Voy a ver si corresponden con algún usuario
          $ssql = "select * from usuario where id_usuario=" . $_COOKIE["id_usuario_dw"] . " and cookie='" . $_COOKIE["marca_aleatoria_usuario_dw"] . "' and cookie<>''";
          $rs = mysql_query($ssql);
          if (mysql_num_rows($rs)==1){
             echo "<b>Tengo un usuario correcto en una cookie</b>";
             $usuario_encontrado = mysql_fetch_object($rs);
             echo "<br>Eres el usuario número " . $usuario_encontrado->id_usuario . ", de nombre " . $usuario_encontrado->usuario;
             //header ("Location: contenidos_protegidos_cookie.php");
          }
       }
    }


    Como primer paso compruebo si existen las cookies con el identificador del usuario y la mencionada marca aleatoria. Además, hacemos una comprobación adicional para ver si alguna de las dos cookies contiene un string vacío, porque en ese caso no están guardadas correctamente y no nos sirven.

    Si todo ha ido bien, miramos en la base de datos si el usuario con identificador determinado en la cookie tiene la marca aleatoria igual que la que tenía la cookie del navegador del visitante. Además, en la consulta en la base de datos también nos aseguramos que la marca aleatoria sea distinta de "", porque entonces es un usuario que nunca había pedido que se guardasen sus datos en el ordenador.

    Si esa consulta daba un registro, es que corresponde con un usuario que se había almacenado en el ordenador y es el usuario que estaba autenticado anteriormente y guardado su acceso.

    Conclusión

    Hasta aquí he comentado todo lo que necesitamos saber para crear la infraestructura para que la página web recuerde la clave del usuario y no tenga que autenticarse cada vez que accede al sitio. El código que hemos mostrado podría completarse con una serie de mejoras o personalizaciones para adaptarlo a nuestras necesidades, pero seguro que sirve de guía para el interesado.

    Ahora presento el código completo de la página de este ejemplo:

    <?
    //conecto con la base de datos
    $conn = mysql_connect("servidor","usuario","clave");
    //selecciono la BBDD
    mysql_select_db("base de datos",$conn);

    //primero tengo que ver si el usuario está memorizado en una cookie
    if (isset($_COOKIE["id_usuario_dw"]) && isset($_COOKIE["marca_aleatoria_usuario_dw"])){
       //Tengo cookies memorizadas
       //además voy a comprobar que esas variables no estén vacías
       if ($_COOKIE["id_usuario_dw"]!="" || $_COOKIE["marca_aleatoria_usuario_dw"]!=""){
          //Voy a ver si corresponden con algún usuario
          $ssql = "select * from usuario where id_usuario=" . $_COOKIE["id_usuario_dw"] . " and cookie='" . $_COOKIE["marca_aleatoria_usuario_dw"] . "' and cookie<>''";
          $rs = mysql_query($ssql);
          if (mysql_num_rows($rs)==1){
             echo "<b>Tengo un usuario correcto en una cookie</b>";
             $usuario_encontrado = mysql_fetch_object($rs);
             echo "<br>Eres el usuario número " . $usuario_encontrado->id_usuario . ", de nombre " . $usuario_encontrado->usuario;
             //header ("Location: contenidos_protegidos_cookie.php");
          }
       }
    }

    if ($_POST){
       //es que estamos recibiendo datos por el formulario de autenticación (recibo de $_POST)

       //debería comprobar si el usuario es correcto
       $ssql = "select * from usuario where usuario = '" . $_POST["usuario"] . "' and clave='" . $_POST["clave"] . "'";
       //echo $ssql;
       $rs = mysql_query($ssql);
       if (mysql_num_rows($rs)==1){
          //TODO CORRECTO!! He detectado un usuario
          $usuario_encontrado = mysql_fetch_object($rs);
          //ahora debo de ver si el usuario quería memorizar su cuenta en este ordenador
          if ($_POST["guardar_clave"]=="1"){
             //es que pidió memorizar el usuario
             //1) creo una marca aleatoria en el registro de este usuario
             //alimentamos el generador de aleatorios
             mt_srand (time());
             //generamos un número aleatorio
             $numero_aleatorio = mt_rand(1000000,999999999);
             //2) meto la marca aleatoria en la tabla de usuario
             $ssql = "update usuario set cookie='$numero_aleatorio' where id_usuario=" . $usuario_encontrado->id_usuario;
             mysql_query($ssql);
             //3) ahora meto una cookie en el ordenador del usuario con el identificador del usuario y la cookie aleatoria
             setcookie("id_usuario_dw", $usuario_encontrado->id_usuario , time()+(60*60*24*365));
             setcookie("marca_aleatoria_usuario_dw", $numero_aleatorio, time()+(60*60*24*365));
          }
          echo "Autenticado correctamente";
          //header ("Location: contenidos_protegidos_cookie.php");
          
       }else{
          echo "Fallo de autenticación!";
          echo "<p><a href='prueba-cookies.php'>Volver</a>";
       }
       
    }else{
    ?>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

    <html>
    <head>
       <title>cookies para detectar usuario</title>
    </head>

    <body>

    <form action="prueba-cookies.php" method="post">
    Usuario: <input type="text" name="usuario">
    <br>
    Clave: <input type="text" name="clave">
    <br>
    <input type="checkbox" name="guardar_clave" value="1"> Memorizar el usuario en este ordenador
    <br>
    <input type="submit" value="Entrar">
    </form>
    <br>
    <br>
    <b>Usuarios válidos:</b>
    <br>
    <br>
       User: pepe
       <br>
       Clave: 1234
       <br>
       <br>
       User: juan
       <br>
       Clave: 1111

    </body>
    </html>

    <?
    }
    ?>


    Por último, dejo aquí el enlace para ver el ejemplo creado en funcionamiento.
  • Autor

    Miguel Angel Alvarez

    Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

    Compartir

    Comentarios

    Jose Manuel

    07/10/2008
    Creo que el problema de este sistema de 'remember password' radica en que estás presuponiendo que el usuario se va a conectar desde el mismo navegador y puesto siempre.

    Quiero decir, imaginemos que un usuario se loguea correctamente y ha señalado la opción de recordar.

    En ese caso estarías metiendo en db un código de cookie que se refescaría a cada logueo, pero si al día siguiente accede desde otro ordenador y le da a recordar, macharía la cookie en db que se insertó en el otro puesto y ya no le funcionaría en el primer ordenador.

    Se podría crear una tabla:

    tabla relaciones:

    id_relacion, id_user, id_cookie

    para solventar el problema.

    De esta forma si el usuario se conecta desde un puesto se guardará en db su relación id, cookie y no machacas la anterior.

    Un saludo.

    Rosillo

    13/11/2008
    Ya que estamos con temas de seguridad, comentar que si manipulo una cookie almacenada en mi ordenador y le asigno por ejemplo el numero 1, que presumiblemente se corresponderá con el id del administrador, la próxima vez que recuerde la sesión entraré como admin.

    Daniel

    14/4/2009
    restrincion de barra de menu por usuario
    Hola amigos

    en este momento trato de hacer una pagina que consulta a la base el acceso para el usuario esta validacion tiene asignado un numero de perfil que permitira ver o no opciones del menu de inicio por ejemplo administrador o usuario

    ahora el problema es que a traves de la seccion me traigo esta valor y lo asigna a la varible pero no se como poder eliminar o desabilitar una opcion del menu

    gracias por su apoyo

    elpampa

    27/4/2009
    Redireccionar a pagina
    Hola, esta muy bueno este articulo, lo que me gustaría saber es como hay que hacer para que un usuario que no inicio seción y quiere entrar a una pagina que no es la de inicio o pricipar, una ves que se lo deriba al la pagina de identificación y carga el usuario y clave esta pagina lo direione a la pagian que quizo entrar inicialmente y no a otra.
    No se si se entiende paro si alguien me entiende por favor denme una mano.
    Muchas gracias

    angel

    02/9/2009
    a PAMPA
    PAMPA: De hecho no es así, ya que también tendrías que saber el valor del campo del BBDD "cookie" para poder manipular también la otra cookie.

    trasgu_kabi

    02/9/2009
    aclaraciones
    PAMPA: De hecho no es así, ya que también tendrías que saber el valor del campo del BBDD "cookie" para poder manipular también la otra cookie.

    Donde pone:
    if ($_COOKIE["id_usuario_dw"]!="" || $_COOKIE["marca_aleatoria_usuario_dw"]!=""){

    no sería mejor
    if ($_COOKIE["id_usuario_dw"]!="" && $_COOKIE["marca_aleatoria_usuario_dw"]!=""){

    trasgu_kabi

    02/9/2009
    Perdón
    me he confundido, la primera aclaración de antes era para ROSILLO

    Hanky

    13/10/2009
    Re:
    En efecto, como dice Jose Manuel, si se abre desde un 2º navegador, la cookie quedaría obsoleta. Una solucion es que si este campo ha sido establecido en la BD. Con lo cual, lo que estamos haciendo es tener una doble contraseña, una para la autentificacion a mano y otra para la identificacion por cookies.

    Esta 2ª contraseña se puede cambiar con la frecuencia que desees, pero has de tener en cuenta que cuando se cambie, en todos los navegadores dejaras de auto-identificarte.

    Yo soy mas tradicional y simplemente guardo el md5 de la contraseña, previamente desordenado(agregar caracteres extra alante, atras y en medio, hacerle operaciones matematicas, hacerle otra vez el md5() u otra operacion de hash, volver a enrevesar...), de tal forma q primero deben adivinar como desordené el md5 para luego usar la fuerza bruta. Me parece que adivinar la clave de un foro no merece tanto esfuerzo, ya que si es alguna aplicación realmente importante, como una entidad bancaria o una pasarela de pago, ¡¡ni pensar en las cookies!! ;)

    Sergio

    26/4/2010
    Fallo en la demo
    Hola, cuando voy al ejemplo publicado al finald e artículo me aparece este error:

    Warning: mysql_num_rows(): supplied argument is not a valid MySQL result resource in /var/www/vhosts/desarrolloweb.com/httpdocs/articulos/ejemplos/php/prueba-cookies.php on line 31
    Fallo de autenticación!

    Volver

    Imagino que será algo que no estará fino con la base de datos, mil gracias por todo.

    jose

    13/6/2010
    base de datos
    me podrias mandar informacionsobre como crear labase de datos en phpmyadmin
    gracias

    jose

    15/6/2010
    campoaleatorio para cookie
    alguienme puede explicar como se configura la bas de datos para el campo de la cookie

    jose

    15/6/2010
    apuntar a la web
    lo siento pero soy nuevo en php:
    como apunto a la web donde quiero entrar o donde coloco el codigo
    gracias

    TheDifs

    09/7/2010
    Error del Ejemplo
    Warning: mysql_num_rows(): supplied argument is not a valid MySQL result resource in /var/www/vhosts/desarrolloweb.com/httpdocs/articulos/ejemplos/php/prueba-cookies.php on line 31

    Fallo de autenticación!


    --
    ---=[ TheDifs ]=---

    sudmiranda

    22/10/2010
    Muy bueno
    Muy bueno el articulo... gracias ahora queda investigar mas para darle muchas mas opciones.

    el7eraldo

    17/3/2011
    warnig
    Hola.
    Primero es lo primero!FELICITACIONES, ESTUPENDO APORTE¡.
    Hara la pregunta: me sale esto al tratar de loguearme con cualquiera de los usuarios validos, sea pepe o juan.
    Este es el mensaje.
    Warning: mysql_num_rows(): supplied argument is not a valid MySQL result resource in /var/www/vhosts/desarrolloweb.com/httpdocs/articulos/ejemplos/php/prueba-cookies.php on line 31
    Fallo de autenticación!
    HAber si lo puedes rebisar porvaor y mandarme una notita explicativa.
    Saludos

    javier

    21/7/2011
    error
    hola ayudam m sale st error al ingresar un usuario Warning: mysql_num_rows() expects parameter 1 to be resource, boolean given in C:xampphtdocsFermin-Tparcial3cokiesprueba-cookies.php on line 32
    Fallo de autenticación!

    yandelfans

    08/9/2011
    felicitaciones
    En primera instancia...gracias......a mi la aplicacion me funciono, pero se le deven hacer unos ajustes.......escribamen ganaranjoo@unal.edu.co os enviare codigo

    felipe213

    29/2/2012
    no funciona
    no funciona el ejemplo

    Dolphin

    08/8/2014
    me gustaron estas paginas me sirvieron de mucho
    es muy buena la informacion de la pagina no mas faltan tutoriales en youtube me agrado mucho

    Eze

    15/3/2015
    Seguridad
    Como garantizo que no copien la cookie y la utilicen en otra maquina?
    Gracias por tu aportacion