domingo, 1 de junho de 2014

Como Criptografar senha no MySQL

Imagem de um cadeado sob a tecla enter de um teclado
Você criptografa suas senhas?
A segurança é um sentimento de proteção que a sociedade necessita, e a forma de trazer esta sensação é através da defesa. Mas como manter essa segurança em época de "Snowden"?

No ambiente web digo que a segurança é ameaçada por dois aspectos, interesse e/ou curiosidade.

  • Interesse

O interesse está ligado a dois fatores, prejudicar alguém ou, para benefício próprio.

Por exemplo, um hacker ao atacar um site ou um sistema, está interessado em prejudicar o atacado ou demonstrar que ele esta vulnerável, ou seja, quer prejudica-lo ou quer tirar vantagem sobre isso. Vantagem, como? Veja isso.
  • Curiosidade
Me responda com sinceridade, você nunca quiz acessar a conta do facebook de alguém? Não quiz ver as conversas do WhatsApp do(a) namorado(a)? (Quero ver as respostas nos comentários hein!)

O sentimento de curiosidade incita a pessoa, e descobrir o que tanto almejada da a sensação de prazer, tudo bem, pode dar raiva, frustração também, mas... Olha o que foi relatado ao Vida de Programador sobre a criptografia de senhas:
Tirinha Vida de Programador

No exemplo, por que motivo você acha que o chefe queria ter a senha descriptografada no banco de dados?

Um motivo para não criptografar as senhas no banco de dados se dá por conta de validar a senha do usuário, ou seja, como comparar a senha do usuário salva no banco de dados com a senha digitado na tela de login?

Muito simples, explico isso em uma tarefa de três passos:

1 - Criar a estrutura (tabela).
2 - Inserir os dados.


3 - Validar a senha armazenada.

1 - Criando a tabela para inserir os dados do usuário de forma criptografada.

Para fins didáticos, nossa tabela contemplará somente três campos, id, login e senha:

 CREATE TABLE `usuario`(   
  `id` BIGINT NOT NULL AUTO_INCREMENT,  
  `login` VARCHAR(20) NOT NULL,  
  `senha` VARCHAR(50) NOT NULL,  
  PRIMARY KEY (`id`)  
 );  

2 - Inserindo valores em nossa tabela de usuários.

Neste momento vamos inserir os registros (senha) já criptografados, mas antes, vou pontuar duas formas de criptografia existentes no MySQL:
Função nativa do MySQL para criptografia de dados, trabalha de forma unidirecional, ou seja, ¹não é possível reverter a informação. Sua entrada de dados é feita através de strings, havendo um retorno também no mesmo tipo de dado.

A função retorna uma string binária de 42 caracteres ou null se o argumento de entrada for nulo.

Minha consideração a você sobre este tipo de criptografia é, não use, mas por que não usar o password como forma de criptografia no MySQL?

Simples, o hash retornado é interpretado pelo MySQL, se você precisar mudar o seu SGBD, este segundo com grande certeza não conseguirá realizar a mesma codificação, o que lhe dará uma grande dor de cabeça.
O MD5 é o algoritmo de criptografia mais utilizado, sendo nativo em vários SGBD's e linguagens de programação, neste formato a problemática que pontuei acima não é encontrada, você pode exportar os dados para outro SGBD e continuará a ter sua autenticação sem problemas. Assim como o password, o MD5 é unidirecional, ¹não sendo possível descriptografar. O resultado da função é um retorno de um hash string binário de 32 dígitos hexadecimais.

Agora que você já sabe sobre dois dos tipos de criptografia do MySQL e sabe qual recomendo, vamos inserir os registros na nossa tabela usuario.

 INSERT INTO usuario  
       (id,  
        login,  
        senha)  
 VALUES (NULL,  
     'admin',  
     MD5('admin'));  

Veja que para criptografar a senha não existe nenhum segredo, somente passe o valor a ser criptografado para a função md5().

3 - Agora, como comparar e validar uma senha criptografada?

No formulário de login, ao encaminhar as informações para o banco de dados a senha já pode estar criptografada, ai é só comparar com a existente, ou pode encaminhar a senha da forma que foi digitada e, no banco de dados criptografa-la e compara-la.

Vou demonstrar de forma a encaminhar a senha como foi digitada e, no banco de dados fazer a criptografia e compara-la, isto para fins de didática, não que esta seja a melhor ou a correta forma de se fazer.

Vamos criar uma função, que passaremos como parâmetro o login e a senha.

 DELIMITER $$  
 DROP FUNCTION IF EXISTS `fun_valida_usuario`$$  
 CREATE FUNCTION `fun_valida_usuario`(p_login VARCHAR(20)  
                , p_senha VARCHAR(50) ) RETURNS INT(1)  
 BEGIN  
 DECLARE l_ret            INT(1) DEFAULT 0;  
     SET l_ret = IFNULL((SELECT DISTINCT 1  
                       FROM usuario  
                      WHERE login = p_login  
                       AND senha = MD5(p_senha)),0);                           
 RETURN l_ret;  
 END$$  
 DELIMITER ;  

Na função verifico se existe na base dados alguma informação que coincida com os valores passados aos parâmetros p_login e p_senha, se existir, a função retornará o valor 1 (Um), caso não, 0 (Zero).

Observe que para comparar a senha usei a função MD5(), passando como valor o parâmetro p_senha.

Fique atento: Veja que não usei aspas ao passar o valor para a  função MD5, isto porque o parâmetro p_senha já é uma string, se você passar o valor p_senha com aspas ('p_senha'), a função irá criptografar o termo p_senha e comparar com o valor que esta no banco de dados, e isto não funcionará.

Com a função criada, só executamos a chamada passando os valores e temos o resultado, validado ou não.

 SELECT fun_valida_usuario('admin','admin') as Validou
validou  
-------
      1  

Como podemos ver, não existe nenhum segredo na criptografia de senhas. Mas se você ficou com alguma dúvida, tem alguma sugestão, correção ou crítica, fique a vontade para comentar.

¹ - Existem informações na internet de que é possível reverter, mas em pesquisas vi na verdade são banco de dados com várias senhas armazenadas de forma descriptografada com seu respectivo hash, o que não concretiza a verdadeira descriptografia.
  • Perguntas Frequentes
Existem outras formas de criptografia de dados no MySQL?
R: Sim, veja mais informações em na página de documentação do MySQL.

Posso criptografar também o login do usuário?
R: Sim, isto pode ser uma regra de negócio, mas isto não algo comum.

Se a senha é criptografada, como faço com os usuários que esqueceram a senha?
R: Simples e seguro, gere uma nova senha e mande para seu e-mail, ou celular. Eu particularmente não gosto de acessar sites que ao acionar a rotina de esqueci minha senha, encaminha para meu email a senha atual, isso me dá uma sensação de insegurança com o site.

Qual o tipo de charset/collation devo usar no campo senha?
R: Recomendo o uso de utf8, utf8_general_ci.

Referencias:

Wikipédia
http://pt.wikipedia.org/wiki/Seguran%C3%A7a_e_defesa

Doc-MySQL
https://dev.mysql.com/doc/refman/5.5/en/encryption-functions.html

Leituras complementares:

Contembits - Criptografando dados no MySQL
Desenvolvedor Interoperável - Criptografia de senhas no MySQL
Thiago Belem - Criptografia no php usando md5, sha1 e base64
Pegasus - Jefferson Araujo - Criptografar senha no MySQL
Código Fonte - Emmanuel - Como criptografar dados no MySQL
Imasters - Júlio César - Criptografia de Senhas
Cláudio Coelho - Criptografia de dados no MySQL

13 comentários:

  1. Respostas
    1. Obrigado pelo feedback, Henrique.

      Grande abraço.

      Excluir
  2. Se eu quiser deixar a disposição dos usuários alterar sua senha a qualquer momento, há algum algoritmo que eu possa usar?

    ResponderExcluir
    Respostas
    1. Olá Wallessa, você pode criar o seu script de alteração de senha, minha sugestão:

      Antes de alterar a senha:

      Em um campo, solicite que a senha atual seja informada, antes de executar a alteração, verifique se a senha atual informada confere com a senha armazenada no banco de dados, esta verificação você pode fazer com o auxílio da próxima fun_valida_usuario que apresentei acima.

      Validada a senha atual, você pode realizar a alteração, gravando a nova senha criptografada, ai você também pode:

      Gravar um log da data de alteração, isto lhe permite criar regras de uso de senhas diferentes, tipo, não permitir usar uma senha que já foi usada a duas alterações anteriores.

      Mandar um e-mail para o usuário informando que sua senha foi alterada, informando que se não foi ele, que contate o administrador e afins.

      Espero ter sanado sua dúvida, mas se ainda precisar, pode contar comigo.

      Excluir
  3. Muito bom, já sei como criptografar senhas no meu banco, mas como faço pra criptografar meu banco de dados?

    ResponderExcluir
    Respostas
    1. Olá Pedro.

      Você poderia dar mais detalhes de o que seria essa criptografia do banco de dados?

      Excluir
  4. Essa lógica de aplica tbm ao banco Postgre?

    ResponderExcluir
    Respostas
    1. Olá François.

      A lógica de tabela e o tipo MD5 funcionam no PG sim, só a função que teria que realizar alguns ajustes.

      Abraços.

      Excluir
  5. Olá muito boa sua explicação. Testei aqui e funcionou a criptografia, mas quando tento acessa no browser, colocando a senha antes de torna-la criptografada, diz que login ou senha inválidos. Alguém poderia me ajudar ou me dizer por que isso esta acontecendo?

    Abraços

    ResponderExcluir
    Respostas
    1. Olá Diel, você esta usando PHP? Caso for, print na tela a senha que esta sendo encaminhada para verificação no banco, pode ser que algo esteja errado antes de mandar a senha pra ser conferida no banco.

      Excluir
  6. EU FIZ DIFERENTE... NO PHPMYADMIN, MODO SQL,

    EU DIGITEI: SELECT MD5('textoAQUI');

    cliquei em Executar e o resultado foi: 6e47d1482eb7d45e9b62c5cfaec06f7d

    ResponderExcluir
  7. como faço para comparar com o tipo PASSWORD?
    meu insert de exemplo: INSERT INTO account SET login = '".$username."', password = PASSWORD('".$password."'), ...
    Como faço pra comparar em PHP?

    ResponderExcluir