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.

DEIXE UMA RESPOSTA