Login em PHP – Conceitos e técnicas

Login em PHP – Conceitos e técnicas

3 de maio de 2018 1 Por Ramos de Souza Janones
Como vender Software - Seja desktop, web ou MobilePowered by Rock Convert
Powered by Rock Convert

Este artigo trata de conceitos e técnicas de criação de um sistema de login em PHP com MySQL de forma segura. 

Para uma autenticação segura é necessário identificar vários pontos.

Alguns itens que deve tomar cuidado:

  • SQL Injection
  • Criptografia de senha
  • Ataque de força bruta
  • Ataques XSS
  • Ataques CSRF
  • Proteger arquivos de sessão
  • Proteger arquivos de sistemas

1 – Iniciando

Defina um hash que será utilizado por todo seu sistema. um exemplo é

define( 'SECURITY_HASH', 'uma frase qualquer ou letras aleatórias com números e simbolos' )

 

2 – Crie sua tabela no banco de dados

Sugestão de tabela

id
email
username
password
keymaster
last_ip
last_access
active
created_at
uptaded_at

 

3 – Registro de usuário

Ao registrar valide se foi passado um email válido, se o campo de username está no formatado correto, e gere uma hash para o keymaster e utilize essa hash para criptografar a senha.

Para validar o email use

if ( filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
    echo "This ($email_a) email address is considered valid.";
}

 

Um exemplo de como gerar o Hash

crypt( rand(99999) . time() .  $_SERVER["REMOTE_ADDR"], SECURITY_HASH );

 

Uma forma mais segura de utilizar esta funcão é utilizar alguns $2a$ no inicio do segundo parâmetro para identificar o tipo de criptografia que deverá ser usada. Ficando da seguinte maneira:

crypt( rand(99999) . time() .  $_SERVER["REMOTE_ADDR"], '$2a$07$' . SECURITY_HASH );

 

Onde $2a$ especifica o algoritmo Blowfish e 07$ o custo para gerar o hash.

ou se estiver usando o php 5.5 poderá usar a função password_hash(). Saiba mais em https://br1.php.net/manual/en/function.password-hash.php

$_SERVER["REMOTE_ADDR"] // Obtem o IP do usuário

 

Armazene o IP do usuário e as datas de último acesso, e quando foi criado.

Deixe o usuário inativo (campo active = 0 ) e valide o email cadastrado enviando um email de confirmação.

No email de confirmação você informa um link do tipo

{hash do resultado da concatenação do keymaster e SECURITY_HASH}

 

Essa url irá mudar o campo active de 0 para 1, indicando que o usuário foi ativado.

4 – Login

blankPara o login você deve receber os dados do formulário e validá-los, verificando se está como o esperado.

Busque as informações do usuário no banco, utilizando apenas o username informado.

Com os dados que obteve do banco, criptografe a senha informada pelo formulário utilizando a keymaster do usuário, o mesmo procedimento do registro.

Compare a senha que obteve do banco com a senha que recebeu do formulário criptografada Caso sejam idênticas, inicie a sessão.

5 – Cuidados básicos

Para obter dados passados via POST ou GET, evite utilizar as variáveis globais puras. Para isso utilize filter_input().

Leia sobre na documentação: https://www.php.net/manual/en/function.filter-input.php

Para garantir que os dados enviados via formulário foi realizado de seu servidor, crie uma hash e armazene em sessão. Adicione essa hash em um campo hidden do formulário. Ao receber os valores do formulário, verifique se o hash que veio do formulário existe e está igual ao da sessão.

Curso de PHP ERP com NFe.

Renove o hash sempre que for exibir um formulário.

Neste link tem um exemplo completo sobre prevenção deste tipo de ataque https://www.owasp.org/index.php/PHP_CSRF_Guard

  • Nunca use o register_globals setado com on
  • Não valide seus formulários apenas no javascript, pois se o usuário estiver com o javascript desativado irá passar direto.
  • Utilize o PDO validando todos as variáveis antes de passá-las para uma SQL.
  • Configure no php.ini o seguinte session.cookie_httponly = 1 . Isso informa para o browser não expor cookies para linguagens clients side como o javascript.
  • Nomeie todos os arquivos com a extensão .php nunca com extensão como .inc, .conf, etc.
  • Evite usar md5 como criptografia, utilize bcrypt pois o algoritmo é muito mais seguro.
  • Guarde a sessão no banco de dados
  • Defina um domínio para seu cookie
  • Contabilize as tentativas de login e o tempo entre elas para evitar robos.
  • Em produção deixe o display_errors como off
  • Gere log de erros e envie para seu email, isso facilita identificar tentativas de invasão.
  • Cuidado com as permissões dos seus arquivos no servidor, garanta que apenas o apache tem permissão sobre eles.

Alguns links úteis.

Powered by Rock Convert

Alguns itens que devemos tomar cuidado:

  • Cookies são fáceis de roubar informações
  • Ataques via falsificação de solicitação entre site ou Cross-site request forgery
  • Reiniciar a sessão e excluir todos os cookies após alteração de senha, para que tudo possa ser recriado.
  • Mesmo que o usuário permaneça ativo, solicitar sempre a senha quando envolver transações financeiras.
  • Nunca armazenar dados de usuários em cookies, como email, senha, cpf, número de cartões, etc.
  • Não basear a segurança apenas no ID de sessão, pois esse ID tambem pode ser clonado.
  • Não utilize dados voláteis ou transferíveis como IP para validar um usuário.
  • Nunca deixar a validade da autenticação eterna.
  • Forneça uma maneira de o usuário desconectar todos os lugares estiverem com a sessão prolongada.

Pensando nos critérios acima citados, podemos chegar no modelo a seguir e minimizar os riscos.

Gerando o token de autenticação

Precisaremos armazenar tokens da autenticação em nosso servidor, para isso uma tabela semelhante deve ser criada em nosso banco de dados.

 - id           // Um identificador para o token, não utilize auto_increment,
                // pois pode trazer problemas, 
 - user_id      // Relacionamento com as informações do usuário
 - token        // Armazena o token de autenticação
 - browser      // Identifica qual browser foi utilizado para autenticar
 - last_access  // Último acesso (timestamp)
 - created_at   // Data de criação (timestamp)

 

LEIA TAMBÉM:  Visual Studio Code em Português pt-br

Para gerar o id desta tabela, podemos utilizar algo simples do tipo

md5( uniqid( mt_rand(), true ) );

 

Nesta solução iremos armazenar tambem o browser utilizado, para dificultar ainda mais as tentativas de ataques, seja por clonagem de sessão, CSRF, ataques automatizados, etc.

No PHP existem funções que facilitam identificar o browser do usuário, mas caso prefira alguma forma alternativa, existem várias na internet. Não importaremos com a versão do browser para evitar erros devido a atualizações automáticas.

Para armazenar o último acesso do usuário, sempre atualize o campo last_access usando a função time().

Para o campo created_at use tambem a função time().

O Token pode ser gerado de forma aleatória, como por exemplo:

sha1( uniqid( mt_rand() + time(), true ) );

 

Toda vez que o usuário autenticar, iremos gerar um novo e armazenar nesta tabela.

No cookie guardaremos apenas o id do token e o token em si.

$id = md5( uniqid( mt_rand(), true ) );
$token = sha1( uniqid( mt_rand() + time(), true ) );

// Armazenar o token na tabela do banco de dados

$expire = ( time() + ( 30 * 24 * 3600 ) ); // O cookie não deve ser eterno.
$cookieToken = array( 
    'i' => $id,
    't' => $token
);
setcookie( 'auth', json_encode( $cookieToken ), $expire, '/', 'www.dominio.com', isset( $_SERVER["HTTPS"] ), true );

 

Validando o token

No código anterior informamos que o cookie só é válido para determinado domínio, porem isso pode ser alterado. Sendo assim devemos validar de onde está vindo a requisição, e a maneira mais simples de fazer isso é usando a variável $_SERVER['REMOTE_HOST'], o domínio deve ser conhecido.

Após validar a requisição, recupere o cookie, deserialize os dados e valide com o banco de dados.

$tokenData = isset( $_COOKIE['auth'] ) ? json_decode( $_COOKIE['auth'] ) : false;
if( $tokenData !== false ) {
    $id = $tokenData['i'];
    $token = $tokenData['t'];

    // Busque no banco de dados o id e valide o token;
    // Veja tambem a validade do token usando como base o campo 'created_at'
    // e o browser.
    // Se tudo estiver correto, apague este token, gere um novo
    // e inicie a sessão.
}

 

LEIA TAMBÉM:  O que é o GZIP e como melhora a velocidade de um site

Incrementando

Para tornar ainda mais seguro, você pode registrar todos os IPs e SESSION_IDs que usaram um determinado token.

Como toda vez que uma nova sessão é iniciada um novo token é gerado, basta gravar em uma segunda tabela o IP que gerou o token e a SESSION_ID, junto destes dados você irá gravar a ultima interação com o seu sistema e o tempo de conexão.

A tabela ficará assim:

 - ip            // Ip do usuário $_SERVER['REMOTE_ADDRE']
 - token_id      // Id do token que foi gerado quando o usuário iniciou a sessão.
 - session_id    // Id da sessão session_id()
 - user_agent    // Informação completa do browser $_SERVER['HTTP_USER_AGENT']
 - time          // Tempo de conexão
 - created_at    // Quando a conexão foi iniciada
 - updated_at    // Última atualização (função time() para toda atualização)

 

Toda vez que o usuário fizer alguma requisição para nosso servidor com a sessão ativa, iremos atualizar o registro da atual conexão dele, recuperando-a pelo . Se nada for recuperado, a sessão dele é inválida e bloquearemos o acesso.

Para atualizar devemos sempre verificar se o IP e o user_agent são os mesmos, assim como podemos verificar se ele possui o token que foi gerado quando a autenticação foi iniciada.

Para atualizar o tempo de conexão, calcule utilizando o campo created_at

time() - $created_at

 

Sempre verifique se existe mais de um IP utilizando um token ao mesmo tempo. Isso pode ocorrer quando o provedor do usuário troca o IP e a sessão se mantém ativa.

Se um token possuir requisições de IPs diferentes com um tempo muito curto entre elas, devemos invalidar o token, e redirecionar qualquer requisição ligada a ele para a tela de login.

Observações

Esta maneira possui algumas falhas, mas já dificulta bastante as tentativas de roubo de sessão devido ao fato de o token ser constantemente atualizado.

As requisições de inserção e atualização ao banco de dados vai aumentar bastante para esse modelo. Para evitar problemas, este modelo deve ser baseado em bancos não relacionais ou utilizar uma camada de cache como o memcached, que irá receber todas as requisições e de tempos em tempos, atualizar o banco de dados.

Devemos alertar sempre o usuário quando mais de uma sessão com estiver ativa, e fornecer a possibilidade de invalidar todos os tokens, e realizar o login novamente.

Nunca devemos possibilitar a troca de senha sem ter uma maneira de validar a autenticidade de quem está alterando.

É muito importante alertar ao usuário sobre o uso de cookies e ter uma política bem escrita sobre isso.

Num próximo artigo mostrarei como criar um sistema de login com Ionic 3 e PHP.

Outros tutoriais e dicas sobre PHP:

 

Vai gostar também:  Os cursos online de programação e tecnologia mais recomendados para 2019

Subscribe to our mailing list

* indicates required


Além de PHP e MySQL, deseja receber outro tema?

LEIA TAMBÉM:  Como pegar as cores de uma imagem via Javascript?

Email Format


Powered by Rock Convert
Siga os bons!
Últimos posts por Ramos de Souza Janones (exibir todos)
vote
Article Rating
Sumário
Login em PHP - Conceitos e técnicas
Nome do artigo
Login em PHP - Conceitos e técnicas
Descrição
Este artigo trata de conceitos e técnicas de criação de um sistema de login em PHP com MySQL de forma segura. 
Autor
Nome
Ramos da Informática
Logo