Depois de ter um sistema de login em PHP a funcionar, pode tornar-se útil fornecer aos utilizadores alguns extras. Por exemplo, no caso de um utilizador se esquecer da sua password, é preciso ter um mecanismo que permita ao utilizador redefinir a sua password.
Neste artigo, vamos ver como criar um sistema de recuperação de password simples, para que o utilizador possa alterar a sua password para uma nova.
Neste caso, para simplificar, vamos admitir que temos na tabela de utilizadores duas informações: e-mail e password codificada (em sha1). Quando o utilizador pede uma recuperação de password, verificamos se esse e-mail existe. Se o e-mail existir, então trata-se de um pedido válido.
Temos então de enviar um link único ao utilizador, para o seu e-mail, para que possa alterar a sua password sem ter de introduzir uma password (visto não a saber). Por exemplo:
http://exameple.net/recuperar.php?utilizador=tobias@mail.com
No entanto, este link não é suficiente. Um atacante poderia simplesmente substituir o endereço de e-mail neste link e assim alterar a password de qualquer conta cujo e-mail fosse conhecido. Assim, precisamos de incluir no link algo aleatório, de modo a permitir que esse link seja usado apenas uma vez. Por exemplo:
http://exameple.net/recuperar.php?utilizador=tobias@mail.com&confirmacao=be254882180cf554fb2a7e05c62ed5a13088500e
O argumento "confirmacao" muda a cada pedido de password, e assim, se este par <utilizador,confirmacao> existir, ele é único, e temos a certeza que o utilizador é o único que tem acesso a este endereço (assumindo que o e-mail utilizado é um canal seguro, mas isso está fora do escopo do artigo). Para guardar este par de valores, precisamos ainda de uma tabela dedicada para o efeito.
Percebida a teoria, vamos à prática!
Antes de mais, é necessário criar a tabela que irá conter os pedidos de recuperação de passwords.
CREATE TABLE recuperacao ( utilizador VARCHAR(255) NOT NULL, confirmacao VARCHAR(40) NOT NULL, KEY(utilizador, confirmacao) )
A chave é composta pelos dois campos, pois desta forma torna-se mais rápido procurar por ambas as informações na base de dados, e não impõe restrições ao utilizador de fazer mais de um pedido de recuperação (por exemplo, no caso do primeiro e-mail ter sido eliminado por engano ou não ter chegado à caixa de e-mail do utilizador).
Vamos criar duas páginas: uma para pedidos de recuperação e outra para alterar a password. Estas páginas terão os nomes perdipassword.php e recuperar.php respetivamente.
perdipassword.php
<h1>Perdi a password</h1> <?php if( !empty($_POST) ){ // processar o pedido mysql_connect('localhost', 'root', ''); // ligar à base de dados mysql_select_db('test'); // escolher a base de dados pretendida $user = mysql_real_escape_string($_POST['email']); $q = mysql_query("SELECT * FROM utilizadores WHERE email = '$user'"); if( mysql_num_rows($q) == 1 ){ // o utilizador existe, vamos gerar um link único e enviá-lo para o e-mail // gerar a chave // exemplo adaptado de http://snipplr.com/view/20236/ $chave = sha1(uniqid( mt_rand(), true)); // guardar este par de valores na tabela para confirmar mais tarde $conf = mysql_query("INSERT INTO recuperacao VALUES ('$user', '$chave')"); echo "INSERT INTO recuperacao VALUES ('$user', '$chave')"; if( mysql_affected_rows() == 1 ){ $link = "http://example.net/recuperar.php?utilizador=$user&confirmacao=$chave"; if( mail($user, 'Recuperação de password', 'Olá '.$user.', visite este link '.$link) ){ echo '<p>Foi enviado um e-mail para o seu endereço, onde poderá encontrar um link único para alterar a sua password</p>'; } else { echo '<p>Houve um erro ao enviar o email (o servidor suporta a função mail?)</p>'; } // Apenas para testar o link, no caso do e-mail falhar echo '<p>Link: '.$link.' (apresentado apenas para testes; nunca expor a público!)</p>'; } else { echo '<p>Não foi possível gerar o endereço único</p>'; } } else { echo '<p>Esse utilizador não existe</p>'; } } else { // mostrar formulário de recuperação ?> <form method="post"> <label for="email">E-mail:</label> <input type="text" name="email" id="email" /> <input type="submit" value="Recuperar" /> </form> <?php } ?>
recuperar.php
<h1>Alterar password</h1> <?php if( empty($_GET['utilizador']) || empty($_GET['confirmacao']) ) die('<p>Não é possível alterar a password: dados em falta</p>'); mysql_connect('localhost', 'root', ''); // ligar à base de dados mysql_select_db('test'); // escolher a base de dados pretendida $user = mysql_real_escape_string($_GET['utilizador']); $hash = mysql_real_escape_string($_GET['confirmacao']); $q = mysql_query("SELECT COUNT(*) FROM recuperacao WHERE utilizador = '$user' AND confirmacao = '$hash'"); if( mysql_result($q, 0, 0) == "1" ){ // os dados estão corretos: eliminar o pedido e permitir alterar a password mysql_query("DELETE FROM recuperacao WHERE utilizador = '$user' AND confirmacao = '$hash'"); echo 'Sucesso! (Mostrar formulário de alteração de password aqui)'; } else { echo '<p>Não é possível alterar a password: dados incorretos</p>'; } ?>
Com este sistema, é possível agora que os utilizadores possam recuperar as suas passwords. Não se esqueçam de perceber o código, e alterar as informações do MySQL e de incluir o vosso formulário de recuperação de password. Reparem ainda que o link é único, e que se o tentarem utilizar novamente, não é possível alterar a password, dando o erro de os dados estarem incorretos.
Em termos de vantagens, podemos enumerar algumas:
Mas também existem desvantagens: