Archive for the ‘Errata’ Category

Usando criptografia no cadastro de usuários

Saturday, November 13th, 2010

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);
	[...]
}