Simples exemplo de como proteger o login da sua aplicação contra brute force.
Você já se perguntou se sua aplicação está segura do ataque de força bruta(Brute Force)?
Imagine um sistema de login. Você quer descobrir a senha do usuário ‘alissonbraga’ por bruteforce logo você testa TODAS as combinações de letras e números até acertar.
Tentativa Manual: Funciona basicamente fazendo a tentativa de um login e senha preenchendo os campos de forma manual, digitando palavra por palavra e efetuando tentativas de login no sistema em questão.
Tentativa Automática: Um software é responsável por pegar palavras salvas em um arquivo, preencher os campos necessários e tentar efetuar login no sistema em questão.
Como resolver de forma simples?
Basicamente o que faremos será criar 2 classes que fará esse controle para nós.
A primeira é uma classe modelo que guardará o IP do usuário, o número de tentativas e um long chamado última tentativa já que trabalharemos com o tempo em milisegundos.
Classe RegistroFalha
import javax.servlet.ServletRequest;
public class RegistroFalha {
private String ip;
private int tentativa;
private long ultimaTentativa;
public static RegistroFalha criarRegistroFalha(ServletRequest request) {
RegistroFalha falha = new RegistroFalha();
falha.setIp(request.getRemoteHost());
falha.setTentativa(1);
falha.setUltimaTentativa(System.currentTimeMillis());
return falha;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getTentativa() {
return tentativa;
}
public void setTentativa(int tentativa) {
this.tentativa = tentativa;
}
public long getUltimaTentativa() {
return ultimaTentativa;
}
public void setUltimaTentativa(long ultimaTentativa) {
this.ultimaTentativa = ultimaTentativa;
}
public long getTempo() {
return System.currentTimeMillis() - ultimaTentativa;
}
RegistroFalha.java
O Próximo passo será criar a nossa classe que fará todo o controle de acesso.
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletRequest;
public class ForcaBrutaControl {
//Map que guardará as tentativas que derem erro
private Map<String, RegistroFalha> tentativaPorIP = new HashMap<String, RegistroFalha>();
private long tempoMilisegundos;
private int numeroTentativas;
/**
* Método que criará um registro para o ip no map caso não exista
* Ou se existir incrementa a falha e seta o hora em milisegundos
* @param request
*/
public ForcaBrutaControl(long tempoMilisegundos, int numeroTentativas) {
this.tempoMilisegundos = tempoMilisegundos;
this.numeroTentativas = numeroTentativas;
}
//Método que criará um registro para o ip no map caso não exista
//Ou se existir incrementa a falha e seta o hora em milisegundos
public void registraFalha(ServletRequest request) {
if (possuiIP(request) && !isTempoExpirado(request)) {
RegistroFalha falha = tentativaPorIP.get(request.getRemoteHost());
falha.setTentativa(falha.getTentativa() + 1);
falha.setUltimaTentativa(System.currentTimeMillis());
} else {
RegistroFalha falha = RegistroFalha.criarRegistroFalha(request);
tentativaPorIP.put(request.getRemoteHost(), falha);
}
}
/**
* Método que verifica se o usuário pode logar validando
* se o usuário tem o ip no Map se o tempo está expirado
* e se não atingiu o limite de tentanvidas
* @param request
* @return
*/
public boolean podeLoggar(ServletRequest request) {
return !possuiIP(request) || isTempoExpirado(request) || !atingiuMaximoTentativas(request);
}
/**
* Verifica se já tem o ip no Map
* @param request
* @return
*/
private boolean possuiIP(ServletRequest request) {
return tentativaPorIP.containsKey(request.getRemoteHost());
}
/**
* Verifica se já não atingiu o limite de tentativas de loggin
* @param request
* @return
*/
private boolean atingiuMaximoTentativas(ServletRequest request) {
return tentativaPorIP.get(request.getRemoteHost()).getTentativa() > numeroTentativas;
}
/**
* Método que verifica se o já passou o tempo desde o bloqueio do usuário
* @param request
* @return
*/
private boolean isTempoExpirado(ServletRequest request) {
return tentativaPorIP.get(request.getRemoteHost()).getTempo() >= tempoMilisegundos;
}
/**
* Método bonus que retorna o tempo restante para o usuário poder tentar logar novamente
* @param request
* @return
*/
public String minutosRestantes(ServletRequest request){
long ms = tempoMilisegundos - tentativaPorIP.get(request.getRemoteHost()).getTempo();
return String.format( "%02d", ( ms / 60000 ) % 60 );
}
}
ForcaBrutaControl.java
Acredito que os comentários dos métodos são suficientes para explicar qual a função de cada um.
Por último chamaremos a classe ForcaBrutaControl no nosso método de login como segue:
//DEFINE NUMERO DE TENTATIVAS DE LOGGIN E TEMPO DE ESPERA
private long tempoMilisegundos = 1200000;
private int numeroTentativas = 3;
private HttpServletRequest request;
public String login() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
request = (HttpServletRequest) externalContext.getRequest();
ForcaBrutaControl control = null;
if (externalContext.getSessionMap().get("ForcaBrutaControl") == null) {
control = new ForcaBrutaControl(tempoMilisegundos, numeroTentativas);
externalContext.getSessionMap().put("ForcaBrutaControl", control);
} else {
control = (ForcaBrutaControl) externalContext.getSessionMap().get("ForcaBrutaControl");
}
if (!control.podeLoggar(request)) {
JSFUtil.addMensagensErro("Seu IP está bloqueado aguarde "+control.minutosRestantes(request)+" minutos e tente novamente.");
return "";
}
//Simula uma busca do usuário no banco de dados
colaborador = dao.autenticaColaborador(email, senha);
if(colaborador != null){
//Prossegue normal no depois do login
return "/home";
}else{
//Usuário não encontrado
control.registraFalha(request);
return null;
}
}
Login
Nesse método a primeira vez que o usuário tentar fazer login será criado e adicionado na sessão o objeto ForcaBrutaControl e será definido qual o tempo de espera e a quantidade de tentativas de acesso.
Após isso a cada tentativa de acesso mal sucedida será incrementada no limite de tentativas do usuário de fazer loggin, após esgotado o limite de tentativas o usuário terá que aguardar 20 minutos (1200000 milissegundos) para tentar acessar novamente.
Qualquer dúvida estou a disposição para responder.
Referência no Livro MundoJ Segurança com Java.
Curtir isso:
Curtir Carregando...
Relacionado