Autenticação de sites com PHP e MySQL

Neste artigo pretendo mostrar uma maneira fácil, simples e segura de fazer um sistema de autenticação em PHP com MySQL. Todas as sessões são registradas em um banco de dados com o IP, data e hora de acesso, deixando um enorme histórico excelente para futuras auditorias. Procurarei detalhar o máximo possível para facilitar a compreensão do artigo.

[ Hits: 52.512 ]

Por: Djair Dutra C. Jr. em 08/12/2010


Criando os primeiros arquivos em PHP



O primeiro arquivo a ser criado será uma página de login, chamada login.php, a qual fará a validação dos dados dos usuários e redirecionará para uma segunda página: a index.php.

Conteúdo da página login.php:

<? # Inicia uma sessão. Esta deve ser obrigatoriamente a primeira linha do arquivo.
session_start();
?>

<? #Grava o número da sessão criada numa variável, chamada $sessid.
$sessid = session_id();
?>

<?
# Verifica se os campos não estão vazios
if ($cara != "" and $codsecreto != "" and $OK != ""){;
  
   #Criptografando a senha digitada com MD5
   $codsecreto = md5($codsecreto);

   # Iniciando a conexão com o banco de dados
   $conexao = mysql_connect('localhost', 'root', 'minhasenha');
   mysql_select_db('nome_do_bd', $conexao);

   # Fazendo a consulta no banco de dados
   $consulta = mysql_query("SELECT codcliente, email, senha, privilegio, status FROM clientes where email='$cara' AND senha='$codsecreto'", $conexao);
  
   # Conta quantos registros foram encontrados
   $linhas = mysql_num_rows($consulta);

   # Se o resultado for apenas um registro: Sucesso!
   if ($linhas == 1){;
      # Pegando dados do cliente
      $codcliente = mysql_result($consulta,0,"codcliente");
      $consulta_email = mysql_result($consulta,0,"email");
      $consulta_privilegio = mysql_result($consulta,0,"privilegio");
      $consulta_status = mysql_result($consulta,0,"status");
   };
  
   #Fechando a conexão após a consulta terminar
   mysql_close($conexao);
};

# Verifica se o código do cliente é maior que zero
# Verifica se o e-mail digitado não é vazio
# Verifica se o e-mail digitado confere com o que foi localizado no banco de dados
# Verifica se o status do cliente está ativo
if ($codcliente >= 1 and $consulta_email == $cara and $consulta_email != "" and $consulta_status == "ATIVO"){;
  
   # Guarda IP da rede numa variável
   $iprede = $_SERVER['REMOTE_ADDR'];

   # Guarda a data numa variável, no formato do MySql (AAAA-MM-DD)
   $hoje_data = date("Y-m-d");

   # Guarda a Hora numa variável
   $hoje_hora = date("H:i:s");

   # Faz a conexão com o banco de dados
   $conexao = mysql_connect('localhost', 'root', 'minhasenha');
   mysql_select_db('nome_do_bd', $conexao);

   # Cria um registro na tabela sessões, com os dados das variáveis acima
   $consulta = mysql_query("INSERT INTO sessoes VALUES('','$hoje_data','$hoje_hora','$ipserver','$iprede','$sessid','$cara','$datafecha','$horafecha','ABERTA')", $conexao);

   # Encerra a conexão
   mysql_close($conexao);

   # Redireciona o cliente para a página index.php
header("Location: index.php");
  
};
?>

Até aqui (acima) os códigos em php tratam apenas das informações recebidas e redirecionam o cliente para uma página index.php, caso os dados estejam corretos. Ainda não temos o código HTML que compõe a página "visualmente". Se colarmos todos os códigos acima no arquivo login.php só aparecerá uma tela branca.

Logo abaixo estão os códigos do HTML para criar os campos de preenchimento do nome e senha. Os códigos HTML devem ser colocados após os códigos em php descritos acima. Como usamos a mesma página (login.php) para logar e autenticar, os códigos em php são lidos primeiro e numa sequência de cima para baixo continuando até chegar ao HTML.

Se no meio deste caminho, houverem informações que conferem com o banco de dados (como um login com sucesso, por exemplo), o php redirecionará o cliente para outra página antes mesmo de ler o código HTML.

Caso as informações não "batam" com as do banco e dados, a leitura da página continuará descendo e como não há nenhum redirecionamento em casos de login errado, a sequência continuará até exibir o HTML novamente, ou seja, retornando para a tela de login.

Resumindo: Se o login obtiver sucesso o php joga o cliente para outra página. Caso contrário exibirá a mesma página para que ele tente fazer login novamente. Veja o código do HTML.

<html>
<head>
<title>Login</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body>
<table width="100%" height="100%" border="0" align="center" cellpadding="0" cellspacing="0">
  <tr>
    <td align="center" valign="middle">
<FORM ACTION=login.php METHOD=POST target=_self>
  Login:
  <input type=text name=cara>
    Senha:
    <input type=password name=codsecreto>
    <input type=submit name=OK value=OK>
  <a href="login.php?">Recuperar Senha</a>
</form>
</td>
  </tr>
</table>
</body>
</html>

Aqui encerramos a nossa página login.php, a qual é responsável por receber os dados digitados e conferir com o banco de dados. Nos campos de login e senha usei as palavras "cara" e "codsecreto", mas você pode colocar qualquer outra, desde que também faça as devidas mudanças no restante do código que se refere a elas.

Explicando a página login.php numa sequência de ações:
  1. O usuário digita o login e senha
  2. A página login.php chama a ela mesma através do <FORM ACTION=login.php METHOD=POST target=_self>
  3. Quando ela recarrega, traz em suas variáveis "cara" e "codsecreto" as informações digitadas para login e senha respectivamente.
  4. De posse destes dados, o php (lá em cima) faz uma consulta ao banco de dados e checa se os dados informados conferem com algum registro no banco de dados de usuários.
  5. Se os dados conferem com algum registro, o php cria um novo registro na tabela sessões, guardando a data, hora, número da sessão e login. Estes dados servem como um histórico de acessos e servem também como base para a consulta das próximas páginas, coisa que veremos mais adiante.
  6. Após conferir que o login está correto e criar o registro na tabela sessões, o php redireciona o cliente para uma outra página, no caso chamada de index.php, mas que pode ser qualquer outra.

Página anterior     Próxima página

Páginas do artigo
   1. Entendendo como as coisas funcionam
   2. Criando o banco de dados e as tabelas necessárias
   3. Criando os primeiros arquivos em PHP
   4. Protegendo cada arquivo individualmente
   5. Criando o arquivo sessoes.php
   6. Como inserir este código nos demais arquivos do sistema?
   7. Finalizando (Dicas e maiores detalhes sobre o artigo)
Outros artigos deste autor

Estamos mais "INGNORANTES"?

O poder do SSH

O que ainda falta pra você abandonar o Windows de vez?

Esqueça tudo e venha para o Linux!

Controle de clientes e acessos no Squid

Leitura recomendada

Segurança em PHP

Dados sensíveis em arquivos com extensão .inc

Instalações PHP não seguras

Vulnerabilidade em formulário PHP

XSS - Um exemplo de ataque

  
Comentários
[1] Comentário enviado por marcrock em 09/12/2010 - 01:52h

Ótimo artigo !!!

Quanto aos arquivos com outras estensões que não php, não existe uma opção que faça o php processar todo e qualquer tipo de arquivo ? Seria meio que um disfarce para o arquivo onde o php intercepta o carregamento e faz todas as verificações como se quele fosse um arquivo php real.

Até mais.

[2] Comentário enviado por sergiogurgel em 09/12/2010 - 11:34h

Não consegui executar. Ele não sai da tela de login Posso lhe enviar o meu projeto para você verificar o por que de não funcionar?

Obrigado.

[3] Comentário enviado por malacker em 09/12/2010 - 13:20h

Caro marcrock,

Existe uma maneira de impedir que certos arquivos sejam visualizados pelo apache. No arquivo de configuração do apache, você pode inserir a regra abaixo, mas como trata-se de um remédio para uma doença que pode ser evitada, acho melhor prevenir (usando só arquivos .php) do que ter que tomar o remédio.
Mas se seu caso exige que seja feita esta medida, espero que este código possa ajudar:

# Impedindo a visualização de arquivos .inc
<Files *.inc>
Order deny,allow
deny from all
</Files>

Se deseja, pode colocar vários arquivos simultaneamente na mesma regra.

# Impedindo a visualização de vários arquivos simultaneamente.
<FilesMatch "\.(gif|jpe?g|bmp|png)$">
Order deny,allow
deny from all
</FilesMatch>

[4] Comentário enviado por reyfernandes em 09/12/2010 - 18:29h

Vou fazer um comentário sem ler completamente seu artigo, li apenas o código do login.php.
Parece que você está com o register_globals no php.ini ligado, fazendo com que qualquer variável enviada por post ou get se torne uma variável no script, ficando fácil burlar o sistema.

pelo que li, talvez acessando:
http://seusite.com.br/login.php?codcliente=1&consulta_email=teste@email.com&cara=teste@email.com&con...
você consiga autorização para sua sessão, podendo navegar em qualquer página php.

Se puder testar e comentar se conseguiu acesso.

[5] Comentário enviado por mago_dos_chats em 09/12/2010 - 20:30h

Bom la vai meu comentario, na página de login você fez a verificação se as variaveis que o cara esta enviando de user e passwd estão em branco, mais se o cara fizer um sql injection?
Pelo jeito que esta seu sistema simplesmente vai bugar e mostrar a estrutura do banco toda.
Sugiro que você valide melhor seu formulário porque como ja dizia aquele ditado de programação .." se entrar lixo, sai lixo.".
Blz.. abraço cara

[6] Comentário enviado por malacker em 10/12/2010 - 12:03h

Não acredito que seja tão fácil burlar este login atravé de técnicas de sql injection porque o php criptografa (MD5) a variável recebida como senha e só depois a insere no banco de dados. Desta forma, qualquer coisa digitada no campo senha não será inserido literalmente no banco de dados, assim como a maioria das técnicas de sql injection.
Depois ele verifica se encontrou apenas só um cliente e só se isso for verdadeiro é que ele "loga" o cara.
O código ainda pode ser melhorado, sim (coisa que eu não fiz). Podemos colocar um "else" após o "if" que verifica se foi encontrado um só registro. Desta forma, dentro desse else, poderíamos zerar a variável $codcliente. Se o cara tentar um ataque por GET ou POST definindo um número para a variável $codcliente, o "if" trataria de zerar esta variável. É uma boa medida de segurança que deixei passar despercebida. Agradeço a quem contribuir.

[7] Comentário enviado por mago_dos_chats em 10/12/2010 - 18:58h

Sim malacker, com o MD5 o o usuario não vai ver qual a senha salva no banco, mais não acha que seria necessário proteger como seu banco esta estruturado? Legal o script para inicio, vai melhorando ele.

[8] Comentário enviado por Allthe em 10/12/2010 - 19:39h

Olá a todos,

Eu alterei o meu código assim:

exten => xxxx,1,Answer ;
exten => xxxx,n,Set(DB(test/count)=1234)
exten => xxxx,n,Set(COUNT=${DB(test/count)})
exten => xxxx,n,Authenticate(${COUNT}) ;

A questão é a seguinte como carreguei a variável test/count=1234. Como posso agora encontra-la no Mysql de modo a altera-la via Mysql?

Alguém pode dar uma ajuda?

[9] Comentário enviado por reideer em 12/12/2010 - 10:41h

Dicas:
nunca faça o que você fez.
#reyfernandes está corretissímo.
além do que, você trata a senha encriptando ela, porém o email ta passando livre livre para injections.

coisa simples a se fazer, sempre que adicionar uma variável a sentença sql, passe o conteúdo pela função addslashes antes. Não resolve todos os problemas, mas não deixa tão na cara o rombo na segurança. Do jeito que está tá na cara do gol.

[10] Comentário enviado por chinlap em 11/03/2011 - 14:39h

Eu li e re-li esse tutorial, fiz o passo-a-passo mas está dando erros nos arquivos. No arquivo sessoes da erro na linha numero 42, e no arquivo login aparece um erro na linha 24. eu não entendo quase nada de php, estou começando nessa area, como concerto esses arquivos? oq devo fazer? me ajudem por favor amigos.
até mais
OBS: esse script funciona também em php5?
Obrigado.


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts