Em um artigo anterior mostrei a você como guardar senhas com segurança no SQL, hoje vamos avançar ainda mais no assunto, aprendendo como proteger ainda mais as credenciais de usuários salgando e apimentando senhas!
É difícil quebrar uma senha?
Apesar dos algoritmos padrão, como MD5, SHA, SHA2, etc… serem indicados quando queremos proteger uma informação, quebrar a segurança deles as vezes pode ser feito em questão de segundos…
Veja o exemplo a seguir: Esse hash (EAB2E06EAFEB391903483173F490BFA38E965488) foi gerado pelo algoritmo SHA para a senha (P4$$word).
Apesar da senha possuir os atributos de segurança que normalmente as empresas exigem (tamanho de 8 caracteres, letras maiúsculas e minúsculas, números e caracteres especiais), usando o site https://hashkiller.co.uk nós podemos decifra-la literalmente em segundos:
Nossa, é assim tão fácil? Dependendo das senhas escolhidas por seus usuários, sim, é assim fácil!
A pergunta que fica é como melhorar a segurança do armazenamento para que a criptografia não seja tão facilmente quebrada dependendo da senha do usuário.
Uma das respostas desse enigma é “salgar a senha”…
Salgando senhas – Sal fixo
Salgar uma senha consiste adicionar texto a informação original (no caso a senha), tornando-a mais complexa e longa, o que dificulta a quebra da segurança por atacantes e piratas cibernéticos.
Veja abaixo um exemplo de salgamento tradicional:
print '--------------------------------------------------' print 'Salgamento tradicional' print '--------------------------------------------------' -- Adicionar informações ao dado a ser protegido, dificultando cracking declare @senha varchar(100) = 'P4$$word' declare @sal varchar(300) = ' "!@#$%&*()_+=/;:?|/<>][{}_-´`abcdefghijklmnopqrstuwxz1234567890áâàäéêèëíìïóôòöúûùüÁÂÀÉÊÈÍÎÌÏÓÔÒÚÛÙÜ' declare @senha_e_sal varchar(300) = @senha + @sal print 'Senha ................: ' + @senha print 'Sal . ................: ' + @sal print 'Senha + sal...........: ' + @senha_e_sal print 'Senha (sha)...........: ' + convert(varchar(300), hashbytes('sha', @senha), 2) print 'Senha e sal (sha).....: ' + convert(varchar(300), hashbytes('sha', @senha_e_sal), 2) print 'Senha e sal (sha2_512): ' + convert(varchar(300), hashbytes('sha2_512', @senha_e_sal), 2) print '' go
Na variável @sal temos uma sequência de caracteres que deixa a senha muito mais longa e complexa.
Enquanto que a senha original (P4$$word) gerava o hash (EAB2E06EAFEB391903483173F490BFA38E965488) que podia ser quebrado em segundos, a senha com sal gera o hash (86B167322324C9442208C038226BC0D9AB34D1EB) que não foi quebrado pelo site que usamos para testes:
Salgando senhas – Sal variável
Particularmente gosto de incluir no sal alguma informação variável, no caso de senhas, costumo incluir o nome ou ID do usuário a qual a senha pertence, criando algo que chamo de “sal variável” ou “salgamento dba-pro”.
No exemplo abaixo, uso o nome dos usuários como parte do sal (a parte variável) em adição a parte fixa (como fizemos no exemplo tradicional acima). Em geral o nome do usuário é uma informação imutável, ainda assim, adiciona grande complexidade na hora de um atacante tentar quebrar a senha.
print '--------------------------------------------------' print 'Salgamento dba-pro: Sal variável + fixo' print '--------------------------------------------------' -- Adicionar "sal variável" para cada registro declare @usuarios table (login varchar(300), senha varchar(300)) insert into @usuarios values ('joao', null) insert into @usuarios values ('maria', null) declare @senha varchar(100) = 'P4$$word' declare @sal varchar(300) = ' "!@#$%&*()_+=/;:?|/<>][{}_-´`abcdefghijklmnopqrstuwxz1234567890áâàäéêèëíìïóôòöúûùüÁÂÀÉÊÈÍÎÌÏÓÔÒÚÛÙÜ' select login, @senha as senha_dos_usuarios, @sal as sal_padrao, @senha + @sal as senha_e_sal, convert(varchar(300), hashbytes('sha2_512', @senha + @sal), 2) as hash_senha_e_sal_padrao, login + @senha + @sal as senha_e_sal_variavel, convert(varchar(300), hashbytes('sha2_512', login + @senha + @sal), 2) as hash_das_senhas_sal_variavel from @usuarios go
Além disso, essa técnica de “sal variável” resolve um problema crítico: No salgamento tradicional, se 2 usuários tiverem a mesma senha, o mesmo hash será gerado.
No salgamento variável os hashes são sempre diferentes, pois os nomes de usuários serão sempre diferentes. Veja acima, João e Maria tem a mesma senha, ainda assim os hashes gerados são diferentes…
Apimentando senhas
O salgamento tradicional deixa uma senha mais complexa adicionando texto fixo a ela.
O salgamento variável adiciona “texto fixo” e “texto variável” a uma senha.
O apimentamento adiciona “texto aleatório” a uma senha!
Josué, isso não é o “salgamento variável” que você acabou de explicar?
Não. Salgamento variável adiciona texto previsível a nossa senha. Por exemplo: O ID ou o login de um usuário é uma informação imutável, relacionada e previsível!
Diferente do salgamento variável, o apimentamento adiciona texto completamente desconhecido a senha! Ou seja, a cada vez que você executa a mesma rotina para um usuário, o texto a ser adicionado a senha será diferente!
Veja abaixo exemplo:
print '--------------------------------------------------' print 'Apimentamento tradicional' print '--------------------------------------------------' -- Adicionar "sal aleatório" para cada registro declare @senha varchar(100) = 'P4$$word' declare @pimenta char(1) = char((abs(checksum(newid())) % (256))) -- Sorteia um caractere da tabela ASC. A-Z (65 a 90) declare @senha_pimenta varchar(300) = @senha + @pimenta declare @hash varchar(300) = convert(varchar(300), hashbytes('sha2_512', @senha_pimenta), 2) print 'Senha ....................: ' + @senha print 'Pimenta...................: ' + @pimenta print 'Senha + pimenta...........: ' + @senha_pimenta print 'Senha + pimenta (sha2_512): ' + @hash -- Como saber se o usuário digitou a senha certa? print '----------------------------------' declare @i smallint = 0 while @i <= 256 begin print 'Código ASC.....................: ' + convert(varchar, @i) print 'Pimenta do código ASC..........: ' + char(@i) print 'Hash guardado (original).......: ' + convert(varchar, @hash) print 'Hash com pimenta a ser testada.: ' + convert(varchar, convert(varchar(300), hashbytes('sha2_512', @senha + char(@i)), 2)) if convert(varchar(300), hashbytes('sha2_512', @senha + char(@i)), 2) = @hash begin print ' --> Senha válida! A pimenta é.: ' + char(@i) + ' (o login foi ' + convert(varchar, @i) + ' vezes mais lento).' break end print '' set @i+=1 end if @i > 256 print '--> Senha ERRADA!' go
EXECUÇÃO #1:
EXECUÇÃO #2:
Observe que para um mesmo usuário e senha, cada execução do código gera uma pimenta diferente, o que gera um hash da senha também diferente.
Se a cada execução a pimenta é torna-se diferente, possivelmente a essa altura você esteja se perguntando: Como um sistema saberá se a senha de um usuário está correta?
A resposta é força bruta! Para saber se uma senha está correta, você terá de testar todas as pimentas possíveis… Abaixo o exemplo de um código que faz exatamente isso.
declare @i smallint = 0 while @i <= 256 begin print 'Código ASC.....................: ' + convert(varchar, @i) print 'Pimenta do código ASC..........: ' + char(@i) print 'Hash guardado (original).......: ' + convert(varchar, @hash) print 'Hash com pimenta a ser testada.: ' + convert(varchar, convert(varchar(300), hashbytes('sha2_512', @senha + char(@i)), 2)) if convert(varchar(300), hashbytes('sha2_512', @senha + char(@i)), 2) = @hash begin print ' --> Senha válida! A pimenta é.: ' + char(@i) + ' (o login foi ' + convert(varchar, @i) + ' vezes mais lento).' break end print '' set @i+=1 end if @i > 256 print '--> Senha ERRADA!' go
Em nosso exemplo a pimenta pode ser qualquer um dos 256 caracteres da tabela ASC.
Logo, para testar se a senha informada é igual a senha armazenada testamos uma a uma as pimentas possíveis, até que encontremos a pimenta correta ou então que testemos todas e identifiquemos que a senha informada está incorreta.
Apimentando senhas – técnica dba-pro
A técnica de apimetamento adiciona grande segurança ao processo de proteção de informações, porém com a segurança temos também um aumento enorme de processamento e tempo de resposta, como você deve ter percebido se testou algumas vezes o código acima.
Como temos 256 possíveis pimentas e precisamos testar uma a uma, um processo de login pode demorar até 256 vezes mais usando o processo de apimentamento.
Para resolver esse lado negativo dessa excelente técnica de segurança, tenho uma dica que chamo de “apimentamento dba-pro”. Consiste em limitar as possíveis pimentas para que tenhamos segurança similar com muito menos perda de performance.
Para o exemplo em questão, ao invés de usar todos os caracteres da tabela ASC, podemos usar apenas os caracteres de ASC160 até ASC190. Dessa forma, temos um processo tão seguro quanto o apimentamento tradicional, porém no máximo 30 vezes mais lento, ao invés de até 256 vezes mais lento:
print '--------------------------------------------------' print 'Apimentamento dba-pro: ínfo única + pimenta selecionada + sal' print '--------------------------------------------------' -- tabela asc parcial (de 161 a 190): declare @a smallint = 161 while @a <= 190 begin print convert(varchar, @a) + '-' + char(@a) set @a+=1 end -- Geração de hash sal + sal variável + pimenta declare @user varchar(100) = 'josue' declare @senha varchar(100) = 'P4$$word' declare @sal varchar(300) = ' "!@#$%&*()_+=/;:?|/<>][{}_-´`abcdefghijklmnopqrstuwxz1234567890áâàäéêèëíìïóôòöúûùüÁÂÀÉÊÈÍÎÌÏÓÔÒÚÛÙÜ' declare @pimenta char(1) = char((abs(checksum(newid())) % (190 - 161))+161) -- Tabela ASC de 161 a 190. declare @senha_pimenta varchar(300) = @user + @senha + @pimenta + @sal declare @hash varchar(300) = convert(varchar(300), hashbytes('sha2_512', @senha_pimenta), 2) print 'Senha ....................: ' + @senha print 'Pimenta...................: ' + @pimenta print 'Senha + pimenta...........: ' + @senha_pimenta print 'Senha (sha2_512)..........: ' + convert(varchar(300), hashbytes('sha2_512', @senha), 2) print 'Senha + pimenta (sha2_512): ' + @hash -- Validação da senha declare @i int = 161 while @i <= 190 begin if convert(varchar(300), hashbytes('sha2_512',@user + @senha + char(@i) + @sal), 2) = @hash begin print 'Senha válida! A pimenta é.: ' + char(@i) + ' (o login foi ' + convert(varchar, @i - 161) + ' vezes mais lento).' break end set @i+=1 end
Senhas, sal e pimenta
Para facilitar seus testes, aqui vai o script completo da aula de hoje:
[sociallocker id=”5114″]
------------------------------------------------------------------ -- Protegendo informações com sal e pimenta... :S ------------------------------------------------------------------ ----------------------------------------------- -- É fácil quebrar uma senha? ----------------------------------------------- -- https://hashkiller.co.uk/sha1-decrypter.aspx -- Hash SHA1: EAB2E06EAFEB391903483173F490BFA38E965488 print convert(varchar(300), hashbytes('sha', 'P4$$word'), 2) print '--------------------------------------------------' print 'Salgamento tradicional' print '--------------------------------------------------' -- Adicionar informações ao dado a ser protegido, dificultando cracking declare @senha varchar(100) = 'P4$$word' declare @sal varchar(300) = ' "!@#$%&*()_+=/;:?|/<>][{}_-´`abcdefghijklmnopqrstuwxz1234567890áâàäéêèëíìïóôòöúûùüÁÂÀÉÊÈÍÎÌÏÓÔÒÚÛÙÜ' declare @senha_e_sal varchar(300) = @senha + @sal print 'Senha ................: ' + @senha print 'Sal . ................: ' + @sal print 'Senha + sal...........: ' + @senha_e_sal print 'Senha (sha)...........: ' + convert(varchar(300), hashbytes('sha', @senha), 2) print 'Senha e sal (sha).....: ' + convert(varchar(300), hashbytes('sha', @senha_e_sal), 2) print 'Senha e sal (sha2_512): ' + convert(varchar(300), hashbytes('sha2_512', @senha_e_sal), 2) print '' go print '--------------------------------------------------' print 'Salgamento dba-pro: Sal variável + fixo' print '--------------------------------------------------' -- Adicionar "sal variável" para cada registro declare @usuarios table (login varchar(300), senha varchar(300)) insert into @usuarios values ('joao', null) insert into @usuarios values ('maria', null) declare @senha varchar(100) = 'P4$$word' declare @sal varchar(300) = ' "!@#$%&*()_+=/;:?|/<>][{}_-´`abcdefghijklmnopqrstuwxz1234567890áâàäéêèëíìïóôòöúûùüÁÂÀÉÊÈÍÎÌÏÓÔÒÚÛÙÜ' select login, @senha as senha_dos_usuarios, @sal as sal_padrao, @senha + @sal as senha_e_sal, convert(varchar(300), hashbytes('sha2_512', @senha + @sal), 2) as hash_senha_e_sal_padrao, login + @senha + @sal as senha_e_sal_variavel, convert(varchar(300), hashbytes('sha2_512', login + @senha + @sal), 2) as hash_das_senhas_sal_variavel from @usuarios go print '--------------------------------------------------' print 'Apimentamento tradicional' print '--------------------------------------------------' -- Adicionar "sal aleatório" para cada registro declare @senha varchar(100) = 'P4$$word' declare @pimenta char(1) = char((abs(checksum(newid())) % (256))) -- Sorteia um caractere da tabela ASC. A-Z (65 a 90) declare @senha_pimenta varchar(300) = @senha + @pimenta declare @hash varchar(300) = convert(varchar(300), hashbytes('sha2_512', @senha_pimenta), 2) print 'Senha ....................: ' + @senha print 'Pimenta...................: ' + @pimenta print 'Senha + pimenta...........: ' + @senha_pimenta print 'Senha + pimenta (sha2_512): ' + @hash -- Como saber se o usuário digitou a senha certa? print '----------------------------------' declare @i smallint = 0 while @i <= 256 begin print 'Código ASC.....................: ' + convert(varchar, @i) print 'Pimenta do código ASC..........: ' + char(@i) print 'Hash guardado (original).......: ' + convert(varchar, @hash) print 'Hash com pimenta a ser testada.: ' + convert(varchar, convert(varchar(300), hashbytes('sha2_512', @senha + char(@i)), 2)) if convert(varchar(300), hashbytes('sha2_512', @senha + char(@i)), 2) = @hash begin print ' --> Senha válida! A pimenta é.: ' + char(@i) + ' (o login foi ' + convert(varchar, @i) + ' vezes mais lento).' break end print '' set @i+=1 end if @i > 256 print '--> Senha ERRADA!' go print '--------------------------------------------------' print 'Apimentamento dba-pro: ínfo única + pimenta selecionada + sal' print '--------------------------------------------------' -- tabela asc parcial (de 161 a 190): declare @a smallint = 161 while @a <= 190 begin print convert(varchar, @a) + '-' + char(@a) set @a+=1 end -- Geração de hash sal + sal variável + pimenta declare @user varchar(100) = 'josue' declare @senha varchar(100) = 'P4$$word' declare @sal varchar(300) = ' "!@#$%&*()_+=/;:?|/<>][{}_-´`abcdefghijklmnopqrstuwxz1234567890áâàäéêèëíìïóôòöúûùüÁÂÀÉÊÈÍÎÌÏÓÔÒÚÛÙÜ' declare @pimenta char(1) = char((abs(checksum(newid())) % (190 - 161))+161) -- Tabela ASC de 161 a 190. declare @senha_pimenta varchar(300) = @user + @senha + @pimenta + @sal declare @hash varchar(300) = convert(varchar(300), hashbytes('sha2_512', @senha_pimenta), 2) print 'Senha ....................: ' + @senha print 'Pimenta...................: ' + @pimenta print 'Senha + pimenta...........: ' + @senha_pimenta print 'Senha (sha2_512)..........: ' + convert(varchar(300), hashbytes('sha2_512', @senha), 2) print 'Senha + pimenta (sha2_512): ' + @hash -- Validação da senha declare @i int = 161 while @i <= 190 begin if convert(varchar(300), hashbytes('sha2_512',@user + @senha + char(@i) + @sal), 2) = @hash begin print 'Senha válida! A pimenta é.: ' + char(@i) + ' (o login foi ' + convert(varchar, @i - 161) + ' vezes mais lento).' break end set @i+=1 end
[/sociallocker]
CONCLUSÃO
Como você deve ter notado, as vezes pode ser um pouquinho chato de salgar e/ou apimentar uma senha ou informação sigilosa, mas não é complexo! Além disso, os termos podem parecer um pouco esquisitos a primeira vista, mas vale a pena dominar as técnicas pois adicionam uma excelente camada extra de segurança a sua aplicação.
Espero que também tenha gostado desse conteúdo.
Abraço do seu amigo Josué 🙂
Boa tarde Josué,
Achei bem legal a ideia de salgar e apimentar =).
Eu havia realizado da seguinte forma:
“Encriptografei” 2 vezes.
Eu peguei o resultado da primeira CRIPTOGRAFIA e CRIPTOGRAFEI novamente.
Sendo assim, acredito que funcione também!
Obrigado!
Boa estratégia também!