terça-feira, 5 de janeiro de 2010

Confirmação de email com PHP (com anti sqlinjection e anti-burlagem)

Há 2 anos implementei um sistema de inscrições em PHP de um grande evento onde milhares de congressistas fazem sua inscrição pela internet. No último ano percebi que muitos congressistas tiveram sérios problemas por não terem cadastrado corretamente seus emails(apesar de eu colocar na tela de cadastro um campo de confirmação de email, alguns congressistas têm a capacidade de cadastrar o email errado duas vezes).
Resolvi aplicar a boa e conhecida técnica de enviar um email de confirmação para os congressistas onde os mesmos poderão confirmar que o email que cadastraram é, de fato, válido.

O algoritmo é o seguinte:
1)Assim que o congressista se cadastra no site eu envio um email com uma mensagem do tipo "clique aqui para confirmar o email cadastrado"
2)O congressista recebe o email e clica no link que passo no corpo do email
3)O script feedback.php processa a requisição de confirmação e registra a confirmação no banco de dados (você pode fazer o que quiser, além de registrar no BD)

Para evitar que qualquer um altere a url indiscriminadamente, implemento um código único que vai com a URL. Trata-se de um hash da URL com uma frase secreta. Isto impede ataques de sqlinjection, tentativas de burlagem do sistema, dentre outras coisas.

Você precisará de dois arquivos cujos códigos estão abaixo:
feedback.php: O arquivo que vai processar a confirmação do email quando o internauta clicar no link que receber por email. Aqui você vai personalizar a ação que quer tomar neste processamento.
hash.php: arquivo que gera a codificação anti-sqlinjection e anti-burlagem. A única coisa que você precisa alterar neste arquivo é a frase secreta.

Vamos ao que interessa:

Para enviar o email com o link de confirmação de email:

1)Montando a URL que será passada por email
  Você precisa identificar, na URL, qual o usuário que está querendo confirmar o email. Provavelmente ele tem um ID na tabela onde está cadastrado. Você deve colocar este ID na URL para que saiba qual usuário processar quando o usuário clicar no link.

include_once("hash.php"); //Carregando hasp.php para possibiltar uso da função gerarHashParametros
$idcadastro = 7; //7 é apenas um exemplo. Coloque aqui o id do congressista obtido no bando de dados.
$scriptfeedback = "http://localhost/teste/feedback.php?".gerarHashParametros("idi=".$idcadastro);

Obs: Se você preferir montar a URL dinamicamente, poderá usar o código abaixo em vez do acima. Este código verifica a URL atual e substitui o nome do arquivo php ( e os parâmetros, caso exista, que estiverem sendo passados a ele) por feedback.php acrescido dos parâmetros
Ex: Se a URL atual for http://microsoft/brasil/meuscript.php?<parâmetros do script>
O código abaixo retorna http://microsoft/brasil/feedback.php?<parâmetros gerados por gerarHashParametros("idi=".$idcadastro)>


$scriptfeedback ="http://".$_SERVER["SERVER_NAME"].substr($_SERVER["SCRIPT_NAME"],0, 1+strrpos($_SERVER["SCRIPT_NAME"],"/"))."feedback.php?".gerarHashParametros("idi=".$idcadastro);


2)Eviando a mensagem com o link para o congressista
  O corpo da mensagem deve ser algo como o abaixo:
 

$mailbody = "Prezado(a) congressista,<br><br>O teu cadastro foi registrado com sucesso às ".date("H:i")."
             horas do dia ".date("d/m/Y").".<br><br>
             Para validar o teu email, por favor <a href=\"".$scriptfeedback."\">clique aqui</a> ou
             copie a URL abaixo e cole no teu navegador de internet.<br>".$scriptfeedback."<br><br>";


Aqui você coloca o código para enviar a mensagem acima para o email do usuário(congressista) cadastrado.
Ex: sendSMTPMail( "bill@microsoft.com", "Confirmação de email", $mailbody);
 

////////////////////////////////////////-------------------///////////////////////////////////////////////////////

Código do arquivo feedback.php
include_once("hash.php"); //Carregando hasp.php para possibiltar uso da função validarHashParametros
$mensagem="";

$parametros = validarHashParametros($_SERVER["QUERY_STRING"]);

if (count($parametros)==0)
{
  echo "Violação de segurança. Certifique-se de ter digitado a URL corretamente.
";
  return;
}

if(!isSet($parametros["idi"]))
{
  $mensagem .= "Impossível determinar a inscrição deste congressista. Por favor,
"; 
  $mensagem .= "tente novamente. Caso o problema persista, entre em contato com o webmaster
";
  $mensagem .= "através do fale conosco.";
}
else
{
  //executar aqui o comando que atualizará o teu banco de dados.
  //Exemplo: "update cadastro set emailconfirmado=1 where idcongressista=".$parametros["idi"];

  $mensagem .= "O teu e-mail foi confirmado com sucesso!
Obrigado por manter seus dados atualizados!

";

}
echo $mensagem;
/*Note aqui que a URL gerada, neste exemplo, foi a seguinte:
http://localhost/teste/feedback.php?idi=7&h0p=18f46b7cd89de7701bb1
Se o internauta quiser burlar o sistema e substituir idi=7 por idi=8 (seja qual for a motivação dele), o sistema bloqueará. Experimente!
*/


////////////////////////////////////////-------------------///////////////////////////////////////////////////////

Código do arquivo hash.php
$wordcode = "&¨%-Minha Frase Secreta!-%¨&";
/*Função usada para, a partir de uma lista de parâmetros de URL,
 *gerar um hash entre esta e o código interno ($wordcode) para evitar
 *mal uso dos scripts de processamento de feedbacks dos congressistas
 *de emails enviados pelo sistema.
 Exemplo de uso:
 $parametros = "idi=1775&ide=3&idc=29";
 Produzirá o seguinte link:
 testejob.php?idi=1775&ide=3&idc=29&h0p=bc2e33fb788c5ac049b8
 */
function gerarHashParametros( $parametros, $hashsize=20 )
{
  global $wordcode;
  return $parametros."&h0p=".substr(md5($parametros.$wordcode),0,$hashsize);
}

/*Função usada para analisar o código hash da URL da página atual, verificando se o mesmo é
  válido e gerado segundo o método gerarHashParametros.
  Exemplo de uso:
  $rc = validarHashParametros( $_SERVER["QUERY_STRING"]);
  echo (count($rc)>0)? "Processado com sucesso!
":"HASH INVÁLIDO!
";
*/
function validarHashParametros( $parametros, $hashsize=20 )
{
  global $wordcode;
  $paramoriginal = substr( $parametros, 0, strpos( $parametros, "&h0p="));
  $params        = explode("&", $parametros);
  $paramsvector  = array();
 
  foreach($params as $param)
  {
    $p = explode("=", $param);
    if(count($p)>0) $paramsvector[$p[0]]=$p[1];
  }

  if( isSet($paramsvector["h0p"]) &&
      (strcmp($paramsvector["h0p"], substr(md5($paramoriginal.$wordcode),0,$hashsize))!=0)
    )
  {
    $paramsvector = array();
  }

  return $paramsvector;
}

Nenhum comentário:

Postar um comentário