Sistema de Login com AJAX e PHP – Blindado

Sistema de Login com AJAX e PHP - BlindadoO tema de hoje é relacionado a segurança, vou demonstrar como construir um sistema de login com AJAX e PHP onde serão implementadas diversas validações.

Objetivo desse post é desenvolver um formulário de login usando AJAX, as informações de usuário e senha serão enviadas para um script PHP, vamos efetuar as devidas validações.

 

Segurança em aplicações WEB

Segurança em Aplicações WEB

Segurança é sempre um assunto delicado ainda mais quando se trata de aplicações WEB, só temos uma certeza nesse assunto “não existe aplicações 100% seguras”.

Porém podemos sempre dificultar a vida do invasor com técnicas de validações e segurança, no blog já publiquei um infográfico com dicas de segurança relacionadas ao PHP, vale a pena ler.

 

Práticas de Segurança na Validação PHP

  1. Verificar a origem da requisição, não é 100% confiável mas vale como medida de prevenção para evitar requisições externas
  2. Validar se o E-mail e Senha foram preenchidos
  3. Validar se o formato do E-mail está correto
  4. Verificar se o IP já excedeu o número de tentativas erradas seguidamente e bloqueiar por 30 minutos o IP, prevenir Brute Force
  5. Consulta no banco de dados parametrizada, prevenindo SQL Injection
  6. Validação da Senha usando API Password Hashing, mais segurança na Senha
  7. Gravar o log de tentativas não validadas, conhecer as tentativas de ataque

 

Construindo sistema de login com AJAX

Construção Sistema de Login

Pré – requisitos:

 

Banco de Dados

Tabela “tab_usuario” possui campos básicos, observem o tamanho do campo “senha VARCHAR(255)” nossa primeira medida de segurança, vamos gerar o hash das senhas usando a API Password Hashing que foi implementada a partir do PHP 5.5, a própria documentação oficial sugere criar campos com tamanho (255) pois o algorítimo pode sofrer mudanças com o tempo aumentando o tamanho da string gerada.

Tabela “tab_log_tentativa” tem como objetivo gravar todas as tentativas de login inválidas e a origem das tentativas HTTP_REFERER, com isso podemos validar a quantidade de tentativas de login, onde chegando a três tentativas erradas vamos bloquear o login para aquele IP por 30 minutos. Com essa tabela também é importante conhecer os tipos de tentativas de login inválidas que nossa aplicação está sofrendo.

 

Formulário de Login HTML

O formulário de login é bem simples, no topo temos uma instrução PHP verificando se já existe um session “logado”, se existir redireciona o usuário para página “home.php”.

No formulário todos os dados serão enviados via AJAX, as instruções jQuery estão no script “custom.js” que chamo no final da página de login.

index.php

 

JavaScript (jQuery)

Nesse script envio os dados e-mail e senha usando AJAX, observem que a requisição espera que a resposta do servidor sempre venha no formato JSON (dataType: ‘json’), com isso recebo o código e a mensagem de erro para exibir no formulário de login. Conheça mais sobre requisições AJAX com jQuery.

Se o script PHP enviar código “1” então redireciono o usuário para página “home.php“, senão exibo a mensagem de erro para manter o usuário informado sobre quantas tentativas ele tem antes de bloquear o IP.

custom.js

 

Validar Login no PHP

Nesse script a coisa fica séria, aqui vamos usar as 7 práticas de segurança que citei no início desse post!

No início defino 2 constantes, “TENTATIVAS_ACEITAS” informa quantidade de vezes seguidas que será aceito tentativas de login “não validadas”, “MINUTOS_BLOQUEIO” informa quantos minutos o IP ficará bloqueado caso o usuário tenha excedido o quantidade de tentativas.

A classe “conexao.php” que é chamada no require é a mesma que usei no post PDO – conexão seguindo padrão Singleton no PHP.

Todas os erros encontrados nas validações abaixo finalizam o script e retornam o código “0” com uma mensagem informativa em JSON para a requisição AJAX.

Dica 1 – Verificar com HTTP_REFERER a origem da requisição, essa informação pode não existir e para os invasores mais avançados é possível alterar no header do request, mas para ataques leves ainda é válida. No meu caso verifico se a requisição veio do formulário de login “http://localhost/login/index.php”, se for diferente disso então finalizo o script.

Dica 2 – Validar se o E-mail e a Senha foram preenchidos, caso não então retorno a mensagem e finalizo o script. Ainda é possível encontrar formulários de login onde a consulta retorna dados mesmo com valores “vazio”, porque o desenvolvedor esqueceu uma linha em “branco” na tabela de usuários.

Dica 3 – Validar se o formato do E-mail está correto usando a função nativa “FILTER_VALIDATE_EMAIL”, é muito comum os usuários esquecerem parte do endereço de e-mail.

Dica 4 – Consultar na tabela “tab_log_tentativa” se o IP do usuário está bloqueado (bloqueado = “SIM”) na data atual por excesso de tentativas, a própria instrução SQL calcula e retorna a quantidade de minutos que já decorreram do bloqueio para verificar se é menor que 30 minutos, então encerra o script enviando a mensagem.

Dica 5 – Consultar se o E-mail existe na tabela “tab_usuario”, sempre usando PDO e trabalhando com consultas parametrizadas para evitar SQL Injection, leia mais posts sobre PDO..

Dica 6 – Se o e-mail seja encontrado então validar se o “Hash” da senha gravada no banco é igual a senha digitada no formulário, observem que para comparação usamos uma função específica da API Password Hashing “password_verify()”, conheça mais sobre a nova API Password Hashing no PHP 5.5.

Dica 7 – Caso não seja validado o usuário então incrementar a SESSION[‘tentativas’], gravar na tabela “tab_log_tentativa” a tentativa e os dados informados. Quando a SESSION[‘tentativas’] chegar ao valor “5” então gravamos o valor bloqueado = “SIM” na tabela “tab_log_tentativa”, a próxima tentativa será bloqueada conforme explicado na Dica 4.

Observação sobre a Dica 7: Alguns tutoriais na WEB ensinam a bloquear tentativas usando somente SESSION, mas dessa forma se o usuário abrir outro navegador no mesmo computador ele vai conseguir tentar novamente, usando banco de dados em qualquer navegador do mesmo computador estará bloqueado. O Bloqueio só ocorre quando o usuário ou invasor errar 5 vezes seguidas email ou senha, senão iriamos bloquear usuários o dia inteiro.

Outros tutoriais ensinam a bloquear por “email” mas quem garante que o invasor vai tentar sempre com o mesmo email, por isso o IP acaba sendo uma boa alternativa.

Após todas essas validações é só verificar se a SESSION[‘logado’] possui o valor “SIM” e retornar o código “1” para requisição AJAX, senão retorna o código “0” e a mensagem informando das tentativas restantes.

login.php

Página Principal

Após logado com sucesso o usuário é redirecionado para “home.php“, nesse exemplo montei uma home bem simples contendo apenas o nome do usuário logado e um link “Sair” direcionando para um script onde será destruído todas as SESSIONs.

No topo da home chamo o script “verifica_sessao.php” que tem como objetivo verificar se o usuário está logado, isso evita que usuários tentem acessar a url da home sem estar logado, o ideal é usar esse script no topo de todas as páginas da aplicação.

home.php

Script PHP para Verificar SESSION

No topo das páginas é verificado se existe um usuário logado chamando esse script, ele é bem simples mas pode receber mais regras conforme a necessidade do leitor.

Apenas verifico se existe a “SESSION[‘logado’]” e se ela possui o valor “SIM”, senão então redireciono o usuário para a página de login “index.php”.

verifica_sessao.php

Script PHP para Logout

Esse script é executado quando o usuário clica no link “Sair” da página home, é necessário poucas linhas para destruir as SESSIONs e redirecionar para a página de login “index.php”.

logout.php

 

Resultado Final

Testando Login

Vou inserir um usuário e gerar o hash usando a nova API:

 

Layout do formulário:

Formulário de Login com AJAX

Validando preenchimento:

Formulário de Login com AJAX validando preenchimento do e-mail

Validando formato do e-mail:

Formulário de Login com AJAX validando formato do e-mail

Digitando senha ou usuário errado na primeira tentativa:

Formulário de Login com AJAX após errar senha

Após errar 5 vezes seguidas usuário ou senha, aviso do bloqueio:

Formulário de Login com AJAX após errar as 5 tentativas

Se logado com sucesso acessa a home:

Paǵina principal após logado com sucesso

Download do exemplo  Exemplo Formulário de Login (3663 downloads) .

Bom pessoal, nesse post demonstrei o caminho para se desenvolver um sistema de login com AJAX utilizando diversos níveis de validações para dificultar invasões, mas como já citei no início não existe sistema 100% seguro. 

Essas validações podem até falhar, mas o invasor terá que suar um pouco rsrs …

Esse script PHP pode ser melhorado conforme a necessidade do leitor, inclusive adicionando algumas validações específicas de cada aplicação. Para o leitor que não optar em usar login com AJAX, poderá trabalhar sem problemas com as submissões de formulário convencionais, basta poucas adaptações.

Até a próxima …

Show Buttons
Hide Buttons