Vou fornecer uma visão um pouco diferente sobre isso.
Eu sempre guardo o sal misturado com o hash da senha salgada.
Por exemplo, colocarei a primeira metade do sal antes do hash salgado da senha e a última metade do sal após o hash salgado da senha. O aplicativo está ciente desse design para poder buscar esses dados e obter o hash salt e salted-password.
Minha justificativa para essa abordagem:
Se os dados de senha / hash forem comprometidos e caírem nas mãos de um invasor, o invasor não saberá o que é o sal olhando os dados. Dessa maneira, um invasor praticamente não pode executar um ataque de força bruta para obter uma senha que corresponda ao hash, pois ele não conhece o hash e não tem como saber quais partes dos dados são partes do sal, ou partes do hash de senha salgada (a menos que ele conheça a lógica de autenticação do seu aplicativo ).
Se o hash da senha salgada for armazenado como está, um ataque de força bruta poderá ser executado para obter uma senha que, quando salgada e com hash, produz os mesmos dados que o hash da senha com sal.
No entanto, por exemplo, mesmo que o hash da senha salgada tenha sido armazenado como está, mas pré-pendente com um único byte aleatório, desde que o invasor não saiba que esse primeiro byte deve ser descartado, isso também aumentaria a dificuldade de ataque. Seu aplicativo saberia descartar o primeiro byte dos dados quando usado para autenticar seu usuário.
A conclusão disso ..
1) Nunca armazene os dados que seu aplicativo de autenticação usa em sua forma exata.
2) Se possível, mantenha sua lógica de autenticação em segredo para aumentar a segurança.
Dê um passo adiante ..
Se você não pode manter em segredo a lógica de autenticação do aplicativo - muitas pessoas sabem como seus dados são armazenados no banco de dados. E suponha que você tenha decidido armazenar o hash da senha salgada misturado com o sal, com um pouco do sal precedido pelo hash da senha salgada e o restante do sal anexado.
Ao gerar o sal aleatório, você também pode decidir aleatoriamente qual proporção de sal armazenará antes / depois do hash da senha salgada.
Por exemplo, você gera um sal aleatório de 512 bytes. Você acrescenta o salt à sua senha e obtém o hash SHA-512 da sua senha salgada. Você também gera um número inteiro aleatório 200. Em seguida, armazena os primeiros 200 bytes do salt, seguidos pelo hash da senha salgada, seguido pelo restante do sal.
Ao autenticar a entrada de senha de um usuário, seu aplicativo passa a string e assume que o primeiro 1 byte dos dados é o primeiro 1 byte do salt, seguido pelo hash salgado. Este passe falhará. O aplicativo continuará usando os 2 primeiros bytes dos dados como os 2 primeiros bytes do salt e repetirá até que um resultado positivo seja encontrado após o uso dos primeiros 200 bytes como os primeiros 200 bytes do salt. Se a senha estiver incorreta, o aplicativo continuará tentando todas as permutações até que nenhuma seja encontrada.
Os profissionais dessa abordagem:
Maior segurança - mesmo que sua lógica de autenticação seja conhecida, a lógica exata é desconhecida no momento da compilação. É praticamente impossível realizar um ataque de força bruta, mesmo com o conhecimento da lógica exata. Comprimentos maiores de sal aumentarão ainda mais a segurança.
Os contras dessa abordagem:
Como a lógica exata é inferida em tempo de execução, essa abordagem é muito intensiva da CPU. Quanto maior o comprimento do sal, mais intensiva a CPU essa abordagem se torna.
A autenticação de senhas incorretas envolverá o custo mais alto da CPU. Isso pode ser contraproducente para solicitações legítimas, mas aumenta a segurança contra invasores.
Essa abordagem pode ser implementada de várias maneiras e pode ser ainda mais segura usando sais de largura variável e / ou hashes de senha salgada.