Hoy vamos a ver como desarrollar de forma sencilla un sistema que nos permita validar de forma segura que un usuario de nuestra aplicación web efectivamente tiene el email que ha indicado en el formulario de registro. El mismo sistema podría servir también para evitar cambios fraudulentos en los perfiles de usuario, para recuperar cuentas cuando se olvidan claves o para solicitar la eliminación de la cuenta de usuario sin temor a gamberradas
.
La idea básica consiste en generar una clave aleatoria durante el proceso de registro, guardarla en la tabla de usuarios (para ello usaremos un campo específico), enviar un email al usuario con un enlace a un método de validación del controlador de usuarios (pasando como parámetros algún identificador del usuario más la clave generada anteriormente), y finalmente, comparar en el mencionado método de validación si la clave pasada a través del enlace corresponde con la clave almacenada en la base de datos. Si dichas claves son iguales entonces se cambia el estado del usuario a "activado" modificando algún campo específico de la tabla de usuarios.
Ahora, antes de pasar a la acción todavía, pensemos en como refinar ligeramente el método para que sea más útil. Si nos fijamos en el último paso, puede parecer que hace falta una columna específica que indique si el usuario ha sido validado o no, pero podremos ahorrárnoslo, y ganar mucho por el camino. Mi idea (y seguro que al de muchos otros) es usar el propio campo dedicado al código de validación. Hay varias formas de hacerlo, pero usaré como ejemplo mi propio procedimiento. Veamos el código y luego analizaremos el proceso más detenidamente.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // app_controller.php ( AppController ) // Generador de claves que usaremos para nuestro sistema de seguridad protected function genPass ($len, $uppercase = true, $lowercase = true, $numbers = true, $sym1 = true) { $up_dict = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; $lo_dict = 'abcdefghijklmnopqrstuvwxyz'; $nu_dict = '0123456789'; $s1_dict = '.-'; $pass_dict = (($uppercase) ? $up_dict : '') . (($lowercase) ? $lo_dict : '') . (($numbers) ? $nu_dict : '') . (($sym1) ? $s1_dict : ''); $dict_size = strlen ($pass_dict); $pass = ''; for ($i=0; $i<$len; $i++) { $pass .= $pass_dict[rand (0, $dict_size-1)]; } return $pass; } |
Nota al canto: he hecho que el generador de claves trabaje solo con un conjunto de 64 tipos de caracteres, esta restricción no tiene mucho sentido, pero me parece un número bonito y lo suficientemente grande. Ahora bien, hay que destacar que ciertos caracteres pueden conllevar problemas, como por ejemplo "+", o ":".
1 2 3 4 5 6 7 8 9 | // Users controller -> Register method (before calling the save method of the User model) $vcode = $this->genPass (62); $this->data['User']['validate'] = 'v:' . $vcode; // Here you should put the code to save the data into the database // and to send an email to the user, with a link like: // http://www.yourapp.com/users/validate_email/username/validatecode // (It's preferible that the user don't know it's user internal id) |
La única modificación sustancial que deberéis hacer en vuestro método de registro es crear la clave antes de guardar los datos de usuario en la base de datos (evidentemente la tabla de usuarios deberá tener un campo "validate"), y evidentemente, no olvidar enviar un email al usuario con el enlace de validación.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | // Users Controller public function validate_email () { if (count ($this->params) < 2) { $this->redirect ('/users/dashboard'); // Or wherever you want } $username = Sanitize::paranoid ( $this->params['pass'][0], array ('_') ); $code = Sanitize::paranoid ( $this->params['pass'][1], array ('-', '.') ); $db_code = $this->User->field ( 'validate', array ( 'username' => $username, ) ); if ($db_code == 'v:'.$code) { $this->User->updateAll ( array ( 'validate' => '\'' . $this->genPass (64) . '\'' ), array ( 'username' => $username ) ); $this->set ('validated', true); } else { $this->set ('validated', false); } } |
Supongamos que tenemos una clave de validación de 64 caracteres, difícilmente podríamos romperla, y aunque nos quedáramos solo con 62 caracteres, seguiríamos con un nivel de seguridad parecido. Aprovechando este detalle, en vez de generar claves de 64 caracteres, las construiremos solo con 62, y añadiremos al principio de estas la cadena "v:" (2 sin contar las comillas). Cuando nuestra aplicación detecte que los primeros dos caracteres de la clave de validación almacenada en la base de datos sean "v:" entonces considerará que el usuario aun no ha sido validado. Una consecuencia de ello es que durante el proceso de validación, una buena forma de dar por validado al usuario sería generar una nueva clave (esta vez de 64 caracteres, y sin el caracter ":").
La nueva clave generada para dar por validado al usuario puede usarse, a su vez, cuando el usuario olvide su clave y la aplicación tenga que enviarle un email, para poder verificar que el método de cambio de clave es llamado desde el email del usuario afectado, y no por una persona ajena a su cuenta (De igual forma, se podría usar para verificar eliminación de cuentas, etc.). Eso sí, nunca debe olvidarse crear una nueva clave cada vez que el sistema de verificación es usado para evitar que la seguridad decaiga.
Espero que os haya resultado útil
, saludos.