Usando criptografia no cadastro de usuários

O aplicativo desenvolvido no livro trabalha inteiramente com a senha do usuário aberta. Porém no tópico 6.8.2 damos uma dica de como configurar o Spring Security para trabalhar com a senha criptografada em MD5. O problema é que esta configuração não é suficiente para que o sistema inteiro trabalhe com a senha criptografada. Foi ai que o leitor Rafael Freitas entrou em contato e solicitou que fizessemos um post explicando como fazer esta alteração, o que faz todo o sentido.

Primeiro uma definição importante: a conhecido algoritmo Base64 na verdade é uma codificação e não criptografia, pois ele é reversível.  Para ser considerada uma criptografia o algoritmo tem que ser inversível, o algoritmo MD5 tem esta capacidade.

De maneira macro, para que o sistema trabalhe com a senha do usuário criptografada algumas alterações são necessárias:

  1. Configuração do framework de segurança para trabalhar com a senha criptografada (já realizado no tópico 6.8.2).
  2. Decidir onde realizar a encriptação da senha do usuário
  3. Encriptar a senha do usuário
  4. Não exibir a senha criptografada em tela ao editar um usuário

Veremos agora o entendimento de cada alteração e depois a alteração passo a passo.

2 Decidir onde realizar a encriptação da senha do usuário

Seguindo o método utilizado no livro de sempre analisar e argumentar bem em qual camada uma alteração deve ser feita, vamos pensar sobre a alteração da senha criptografada.

A camada de acesso a dados (UsuarioDAO) deve ser imediatamente descartada, pois como já foi explicado esta camada apenas transfere dados do sistema para o banco de dados, não deve fazer qualquer alteração ou transformação.

A camada de regra de negócio (UsuarioRN) é uma forte candidata, mas como a senha do usuário será criptografada e esta criptografia é irreversível significa que a senha do usuário entrará nela aberta  (ao salvar) e sairá criptografada (ao consultar). Acredito que isto pode causar alguma confusão. O ideal é que esta camada sempre trabalhe com a senha em um formato único.

Desta forma, a camada de visualização é o local correto para fazer a alteração e controlar a criptografia. Ela obrigatoriamente irá trabalhar com a senha aberta, pois é o que o usuário digita na tela, mas assim que receber esta senha deverá encriptar e enviar adiante para salvar, o que garante uma maior segurança.

3 Encriptar a senha do usuário

A encriptação da senha do usuário pode ser feita com uma classe do próprio Spring Security, conforme o exemplo a seguir:

import org.springframework.util.DigestUtils;

String senhaAberta = “teste123”;
String senhaCripto;
senhaCripto = DigestUtils.md5DigestAsHex(senhaAberta.getBytes());
System.out.println(senhaCripto);

Resultado

aa1bf4646de67fd9086cf6c79007026c

4 Não exibir a senha criptografada em tela ao editar um usuário

A partir do momento que o sistema começar a trabalhar com a senha criptografada não vai mais fazer sentido enviar esta senha para a tela, para alteração, como já é atualmente.

Não faz sentido pois pode causa confusão por dois motivos:

  • A senha em MD5 é bem maior que a senha real digitada. Se o usuário digitar uma senha de 5 caracteres, em MD5 ela terá 32 caracteres. Se ao alterar um registro o usuário perceber esta senha bem maior vai estranhar e vai achar que está errado.
  • Enviar a senha criptografada para a tela fará com que o campo de senha as vezes tenha uma senha aberta e outras vezes uma senha criptografada, com certeza é confuso.

Ou seja, se for o cadastro de um novo usuário a senha será preenchida normalmente, ser for uma alteração de usuário o campo senha deverá ser mantido em branco e o usuário somente preencherá este campo de quiser alterar a senha, senão deixa em branco.

Com isso definimos um comportamento do sistema: o campo de senha só será preenchido com senha aberta.

Porém se a senha for deixada em branco (significando que o usuário quer manter a senha antiga), como vamos salvar o registro do usuário se esta senha está em branco?

O segredo aqui é guardar a senha criptografada do usuário em outro local, quando a tela for aberta para edição (veremos como fazer no passo a passo).

Para manter o campo da senha em branco e considerar que ele só será preenchido quando o usuário quiser alterar existem duas opções:

1. Colocar um aviso junto ao campo dizendo: Preencher somente se quiser alterar.

2. Usar o componente do Primefaces WaterMark, que mostrará a mesma mensagem anterior, só que dentro do campo. http://www.primefaces.org/showcase/ui/watermark.jsf

Fazendo a alteração passo a passo

1. Crie uma nova propriedade String senhaCriptografada na classe UsuarioBean, gerando os métodos get e set.

2. Altere os campos de senha de tela para não serem obrigatórios caso seja uma edição de usuário. É possível identificar isto alterando o atributo  required dos campo Senha e Confirmar Senha para:
required=”#{empty contextoBean.usuarioLogado}”

3. Altere os campos de senha da tela para não re-exibirem a senha, configurando o atributo redisplay=”false”.

4. Altere o método editar() para armazenar a senha atual na propriedade senhaCriptograda.

5. Altere o método salvar() para recuperar a senhaCriptografada (senha = senhaCriptografada) caso esta não senha sido preenchida em tela, e para criptografar a senha caso tenha sido preenchida em tela.

6. Inclua na tela usuario.xhtml um campo inputHidden armazenando a senhaCriptografada:
<h:inputHidden value=”#{usuarioBean.senhaCriptografada}”/>
A classe UsuarioBean (métodos editar e salvar) ficará conforme o exemplo a seguir:

public String editar() {
    this.senhaCriptografada = this.usuario.getSenha();
    return "/publico/usuario";
}

public String salvar() {
	FacesContext context = FacesContext.getCurrentInstance();

	String senha = this.usuario.getSenha();
	if (senha != null &&
            senha.trim().length() > 0  &&
            !senha.equals(this.confirmarSenha)) {
		FacesMessage facesMessage = new FacesMessage("A senha não foi confirmada corretamente");
		context.addMessage(null, facesMessage);
		return null;
	}
	
	if (senha != null && senha.trim().length() == 0) {
		this.usuario.setSenha(this.senhaCriptografada);
	} else {
		String senhaCripto = DigestUtils.md5DigestAsHex(senha.getBytes());
		this.usuario.setSenha(senhaCripto);
	}

	UsuarioRN usuarioRN = new UsuarioRN();
	usuarioRN.salvar(this.usuario);
	[...]
}

Tags: , , , , , ,

7 Responses to “Usando criptografia no cadastro de usuários”

  1. Rodrigo de Macêdo Ferreira Says:

    Só queria registrar que o md5 é reversível sim. com uma simples busca no google pela reversão de uma string embaralhada pelo md5 mostra um site que faz isso…

    Mas belo post!

  2. Décio Heinzelmann Luckow Says:

    Olá Rodrigo. O que encontrei foram sites que procuram o hash md5 na internet ou em um banco de dados pela string original que o gerou. Por exemplo, o hash utilizado no post é uma senha muito comum “teste123”, nem é preciso reverse pra encontrar outros sites que utilizaram o mesmo exemplo.

    Outro exemplo, o hash da palavra “xpto999” nenhum site conseguiu reverter, mas o hash da palavra “dog” todos conseguiram. Simplesmente porque são palavras comuns e que já estavam no ‘dicionário’.

    Mas você tem razão, apesar de não ser uma reversão perfeita, ele consegue resolver alguns casos, o que diminui a segurança do MD5. Assim, de qualquer forma, as antigas regras de segurança de senha garantem uma segurança maior.

    Mas é isso, nenhuma segurança é perfeita, elas só conseguem dar mais trabalho para quem quer quebrar. Talvez eu não encontrei este site exato que você comentou, tente validar isso que eu falei, se você tiver o site que faça a reversão exata mande mais um comentário.

    agradeço a sua contribuição

    abraços

    Décio

  3. Carlos Willians Says:

    Boa tarde, amigos.

    Primeiramente quero parabenizá-los pela bela obra. Só que estou encontrando alguma dificuldade no código do capítulo 3 da locadora. Digitei o código do livro, fiquei três dias quebrando a cabeça, rodei o script do banco, copiei o código fonte para a IDE e mesmo assim não cadastra. Não sei o que está acontecendo e pediria que fosse feita uma revisão.

    Abraços!

  4. Décio Heinzelmann Luckow Says:

    Olá Carlos, desculpe a demora, acabei ficando alguns dias sem verificar os comentários do blog. Acabei de instalar um forum para os leitores tirarem dúvidas sobre os capítulos, peço que direcione as suas dúvidas no forum para lhe respondermos e para que suas dúvidas possam ajudar outras pessoas também.

    http://www.javaparaweb.com.br/forum

    Mais uma vez desculpe a demora, de qualquer forma, precisaremos de mais informações sobre o erro, no forum publique o descritivo das exceções que ocorreram. Até agora os leitores não tem apresentado dificuldade neste capítulo, então acredito que não seja erro no livro.

    abraços

    Décio Luckow

  5. Luiz Paulo Monteiro Says:

    VALEU, funcionou, só uma ressalva no banco de dados mudem o tamanho do campo de 10 para 32 ….Abraçosssssss

  6. TeeK Says:

    Parabéns pelo livro!!

    Uma pergunta : Há como CRIPTOGRAFAR a senha do banco usada no XML ??

    root

  7. Décio Heinzelmann Luckow Says:

    Olá, naturalmente não tem como criptografar, mas tem como você guardar a senha criptografada em outro lugar e setar no hibernate programaticamente, veja este post: http://www.guj.com.br/java/33071-criptografar-a-senha-de-acesso-no-arquivo-de-configuracao-do-hibernate

Leave a Reply

*