#!/intro
A autenticação por palavra-passe continua a ser um dos métodos mais comum de controlo de acesso a sistemas digitais. A sua popularidade deve-se à facilidade de implementação e à familiaridade junto dos utilizadores. No entanto, esta simplicidade acarreta um conjunto de fragilidades que, se não forem devidamente tratadas, podem comprometer seriamente a segurança dos sistemas.
A robustez deste método de autenticação depende, em grande medida, da forma como as palavras-passe são protegidas. Entre os diversos mecanismos de proteção disponíveis, a dispersão criptográfica, ou hashing, destaca-se como uma técnica essencial para impedir a exposição direta de dados de acesso em caso de violação.
No entanto, a utilização exclusiva de funções de dispersão criptográfica genéricas é insuficiente. Estas foram concebidas para privilegiar a velocidade e a eficiência computacional, o que as torna especialmente vulneráveis a ataques automatizados.
Uma proteção eficaz requer a utilização de funções especificamente concebidas para o armazenamento seguro de credenciais. Estas incorporam mecanismos de variabilidade e de limitação da paralelização do cálculo, de forma a reduzir significativamente a eficácia de ataques por enumeração.
Uma abordagem inadequada à dispersão criptográfica de palavras-passe compromete diretamente a robustez do sistema de autenticação e, por consequência, a segurança da aplicação que dele depende.
> ameaças
O armazenamento de palavras-passe constitui um dos vetores de ataque mais frequentemente explorados em sistemas digitais.
Uma das vulnerabilidades mais críticas é a exposição direta de credenciais, frequentemente resultante do seu armazenamento sem qualquer forma de proteção criptográfica ou de falhas nos mecanismos de proteção implementados. Quando ocorre uma violação, essas palavras-passe podem ser imediatamente utilizadas para comprometer outros serviços, sobretudo em cenários de reutilização de credenciais.
Outra ameaça comum é a vulnerabilidade a ataques por tentativa, como os por exaustão ou de dicionário. Quando os dados são armazenados de forma que permita verificações rápidas e massivas, torna-se viável testar grandes volumes de combinações até encontrar correspondências válidas.
A uniformidade nos valores armazenados representa também uma ameaça significativa. Se diferentes utilizadores com palavras-passe iguais gerarem exatamente o mesmo valor armazenado, torna-se possível identificar padrões e reduzir significativamente o esforço necessário para a recuperação de credenciais a partir dos dados expostos.
A exposição involuntária dos dados de autenticação através de canais secundários, como ficheiros de registo, cópias de segurança ou ambientes de testes, constitui uma fragilidade adicional frequentemente negligenciada.
Mesmo em sistemas tecnicamente sólidos, a divulgação destes dados pode anular todas as outras medidas de proteção.
Por fim, a exploração de comportamentos subtis no processamento das credenciais, como diferenças de tempo nas operações de verificação, pode ser usada para inferir informação sensível e facilitar o processo de quebra.
Estas vulnerabilidades, embora conceptualmente simples, continuam a ser exploradas com sucesso devido a erros de conceção, configuração ou gestão.
O armazenamento de palavras-passe exige, por isso, um cuidado extremo e uma abordagem informada desde a fase de desenho do sistema.
> dispersão_criptografica
A dispersão criptográfica é um processo determinístico e unidirecional que converte uma entrada de tamanho arbitrário num valor de tamanho fixo. Este valor, também designado por resumo ou hash, representa de forma única a informação original, sem permitir a sua recuperação prática.
Ao contrário da cifra, que é um processo reversível controlado por uma chave e destinado a garantir a confidencialidade, a dispersão não utiliza qualquer segredo e não permite reverter o valor gerado para obter a informação inicial.
Os algoritmos de dispersão criptográfica são concebidos para tornar a recuperação dos dados originais computacionalmente inviável, assegurando assim que o valor de dispersão não revele o conteúdo original.
Enquanto a cifra transforma os dados para os proteger e possibilita a sua recuperação posterior por utilizadores autorizados, a dispersão transforma os dados para os representar de forma resumida e irreversível, permitindo apenas verificar se a entrada corresponde ao valor gerado.
No contexto do armazenamento de palavras-passe, permite verificar se uma palavra-passe fornecida corresponde à representação armazenada, sem que seja necessário guardar ou recuperar a credencial original.
> propriedades
Uma função de dispersão criptográfica genérica deve satisfazer um conjunto de propriedades fundamentais que assegure a sua robustez e adequação a contextos de segurança.
A unidirecionalidade, ou resistência à pré-imagem, é a propriedade que permite calcular o valor de uma função de forma eficiente, mas que torna computacionalmente inviável inverter esse processo. Esta característica é essencial na proteção de palavras-passe, pois impede que um atacante recupere a palavra-passe original a partir do valor de dispersão armazenado.
A resistência à segunda pré-imagem é a propriedade que garante que, conhecendo uma determinada entrada e o respetivo valor de dispersão, não seja computacionalmente viável encontrar uma outra entrada distinta que produza o mesmo valor. Esta propriedade é essencial para garantir que não é possível criar uma palavra-passe alternativa que seja aceite como equivalente à original.
A resistência a colisões é outra propriedade igualmente relevante,torna computacionalmente inviável encontrar duas entradas distintas que produzam o mesmo valor de dispersão. Embora num sistema bem construído não deva ser permitido submeter diretamente valores de dispersão, esta propriedade protege contra cenários em que seja possível manipular dados autenticados, como tokens ou assinaturas.
O efeito avalanche assegura que uma alteração mínima na entrada, como a modificação de um único carácter, produza um valor de dispersão completamente diferente. Esta propriedade dificulta significativamente ataques por aproximação ou tentativas incrementais, ao eliminar qualquer relação previsível entre entradas semelhantes e os seus resultados.
O determinismo é outra propriedade fundamental das funções de dispersão. Assegura que, para uma dada entrada, o valor de dispersão será sempre idêntico, independentemente do número de vezes que a função for aplicada. Esta previsibilidade é essencial para permitir comparar, de forma segura e consistente, a palavra-passe introduzida com o valor de dispersão previamente armazenado, sem guardar a original.
No contexto do armazenamento de palavras-passe, a eficiência controlada é uma propriedade particularmente relevante. Funções desenvolvidas especificamente para o armazenamento de credenciais devem permitir ajustar o esforço computacional necessário, tornando o processo de cálculo do valor dispersão intencionalmente mais demorado. Este comportamento tem como objetivo dificultar ataques automáticos em larga escala, sem prejudicar de forma significativa o desempenho em cenários legítimos.
A resistência à paralelização é outra das propriedade fundamentais em funções dedicadas ao armazenamento de palavras-passe. Estas devem ser concebidas para impor exigências significativas de memória e carga computacional, limitando a eficácia de execuções simultâneas. Este comportamento torna os ataques em larga escala, baseados em tentativas paralelas, consideravelmente mais dispendiosos e ineficientes.
Por fim, funções de dispersão concebidas especificamente para o armazenamento de credenciais precisam de ser capazes de resistir à otimização em hardware especializado, dificultando implementações eficientes em dispositivos como GPU ou circuitos dedicados. Devem, para isso, adotar estruturas de cálculo que, pela sua natureza, não favoreçam a aceleração em plataformas de hardware otimizado, tornando os ganhos de desempenho nesse tipo de ambiente praticamente inviáveis.
> salt
O salting é uma técnica fundamental no reforço da segurança da dispersão de palavras-passe. Consiste na introdução de um valor aleatório, designado por salt, que é concatenado à palavra-passe antes de ser submetida à função de dispersão criptográfica. Este valor deve ser único por utilizador e por palavra-passe, e não precisa de ser secreto.
A principal função do salt é quebrar a uniformidade entre entradas iguais. Sem salting, duas palavras-passe idênticas originam exatamente o mesmo valor de dispersão, o que permite a um atacante identificar reutilização de credenciais e aplicar ataques otimizados, como o uso de tabelas pré-computadas (rainbow tables). Com um salt único por entrada, estas abordagens tornam-se inviáveis, pois obrigam ao cálculo de novos valores de dispersão para cada combinação possível de salt e palavra-passe.
Além disso, o salting aumenta significativamente a entropia do processo de derivação do valor de dispersão. Mesmo que um atacante tenha acesso a uma base de dados de valores de dispersão, a presença de salts diferentes impede a reutilização direta de ataques aplicados noutros sistemas ou utilizadores.
O salt é geralmente armazenado em texto simples, associado ao respetivo valor de dispersão. A sua função é garantir que cada entrada no sistema seja tratada de forma isolada, mesmo que as palavras-passe sejam iguais. Para garantir eficácia, o salt deve ter um comprimento adequado e possuir entropia suficiente. Deve ser produzido com um gerador verdadeiramente aleatório e criptograficamente seguro.
Sem salting, mesmo as funções de dispersão criptográfica mais robustas tornam-se vulneráveis a ataques sistemáticos. O salt não resolve todos os problemas de segurança, mas é um elemento indispensável numa estratégia de defesa efetiva no armazenamento de palavras-passe.
> PBKDF2
O PBKDF2 (Password-Based Key Derivation Function 2) é uma função de derivação de chaves definida pelo standard PKCS #5 (RFC 8018). Foi uma das primeiras soluções padronizadas para dispersão criptográfica segura de palavras-passe. Concebida para transformar palavras-passe, normalmente fracas ou de baixa entropia, em chaves criptográficas resistentes, através de um processo repetido e parametrizável.
A função baseia-se num algoritmo de dispersão criptográfica com chave aplicado iterativamente sobre a combinação da palavra-passe com um salt. O objetivo é introduzir um custo computacional significativo, tornando cada tentativa de adivinhação ou quebra deliberadamente lenta. Isto dificulta ataques automáticos reduzindo significativamente a velocidade de ataques por tentativa.
O PBKDF2 permite ajustar o número de iterações, influenciando diretamente o tempo necessário para o cálculo de cada valor de dispersão. Este fator de trabalho deve ser dimensionado de acordo com a capacidade do sistema e os requisitos de segurança: valores baixos como por exemplo, 1 000 ou 10 000 iterações, são hoje considerados inseguros. Atualmente, é recomendada a utilização de valores na ordem das centenas de milhar a vários milhões, dependendo do contexto e da sensibilidade dos dados protegidos.
Apesar de amplamente suportado e ainda aceitável em ambientes regulados, o PBKDF2 apresenta limitações. A função é computacionalmente intensiva mas não consome memória de forma significativa, o que a torna vulnerável a ataques massivos com hardware especializado, como GPUs, FPGAs ou ASICs.
Por este motivo, o PBKDF2 é frequentemente considerado adequado apenas em cenários com fortes restrições de interoperabilidade ou conformidade, sendo ultrapassado por algoritmos mais modernos no que respeita à resistência a ataques distribuidos.
> bcrypt
O bcrypt é uma função de dispersão criptográfica concebida especificamente para proteger credenciais contra ataques automatizados. Foi introduzido em 1999 como uma extensão do algoritmo Blowfish e inclui, desde a sua origem, mecanismos para dificultar os ataques por tentativa e a paralelização. A sua implementação original surgiu no sistema operativo OpenBSD, onde foi adotado como método padrão para o armazenamento seguro de palavras-passe.
A principal característica do bcrypt é a introdução de um fator de custo configurável, que determina o número de iterações internas executadas durante o processo de derivação. Este parâmetro define o tempo necessário para calcular cada valor de dispersão e permite ajustar a função ao poder computacional disponível à data, mantendo o esforço de ataque constante ao longo do tempo.
O bcrypt inclui também um salt de 128 bits, gerado de forma aleatória para cada palavra-passe. Este valor é incorporado no resultado final armazenado, o que assegura a variabilidade entre palavras-passe idênticas, mesmo quando utilizadas no mesmo sistema, dificultando ataques com recurso a tabelas pré-computadas.
O valor armazenado em bcrypt inclui a versão do algoritmo, o factor de custo, o salt e o valor de dispersão resultante, encapsulados num formato padronizado e codificados em Base64 modificado.
Exemplo de valor gerado com bcrypt para password
$2a$12$ZVirbkTwcQzgWUOra0Dqb.GCRJrYsIiBMXDb8zTamiD1luObJmWDK
Este valor contém quatro componentes codificados:
$2b$: Identificador da versão do algoritmo bcrypt utilizada (neste caso, versão 2b).
12$: Fator de custo (número de iterações internas é 2^12 = 4096).
ZVirbkTwcQzgWUOra0Dqb.: Salt aleatório codificado.
GCRJrYsIiBMXDb8zTamiD1luObJmWDK: valor de dispersão derivado.
Durante a autenticação, a aplicação extrai o salt e o fator de custo do valor armazenado, aplica bcrypt à palavra-passe fornecida e compara os resultados.
Apesar da sua robustez comprovada, o bcrypt apresenta algumas limitações técnicas.
Uma das principais limitações está no comprimento efetivo da palavra-passe, que é limitado a 72 bytes. Qualquer conteúdo que ultrapasse esse limite é truncado de forma silenciosa. Esta característica representa um risco, sobretudo em contextos que utilizam passphrases longas, podendo comprometer a entropia esperada e facilitar ataques por redução do espaço de procura.
Outra limitação relevante resulta do facto de o algoritmo não ser formalmente padronizado. A existência de diversas variantes informais do protocolo, como 2a, 2b, 2x e 2y, introduzidas por diferentes implementações para corrigir falhas ou comportamentos ambíguos, compromete a interoperabilidade entre sistemas e dificulta uma validação consistente dos resultados.
Além disso, tal como o PBKDF2, o bcrypt é vulnerável a ataques paralelos com recurso a hardware especializado. Embora permita ajustar o custo computacional, a sua reduzida exigência em termos de utilização de memória facilita a replicação eficiente em plataformas dedicadas, como FPGAs ou ASICs.
Mesmo com estas limitações, o bcrypt continua a ser amplamente utilizado e considerado seguro em diversos contextos. A sua resistência à paralelização, simplicidade de integração e compatibilidade com sistemas existentes tornam-no numa opção fiável para o armazenamento de palavras-passe em ambientes de produção.
> scrypt
O scrypt foi concebido para ultrapassar limitações de funções como o PBKDF2 e o bcrypt, que, apesar de introduzirem um custo computacional ajustável, são pouco exigentes em termos de memória.
Definido no RFC-7914, o scrypt introduz um modelo de derivação que combina custo computacional com consumo intensivo de memória. Ao forçar cada derivação de chave a utilizar uma quantidade significativa de memória temporária, o scrypt dificulta a execução paralela e a replicação eficiente do processo em hardware dedicado.
Este comportamento baseia-se em três parâmetros principais: o factor de custo computacional (N), o factor de utilização de memória (r), e o grau de paralelismo (p).
Exemplo de valor gerado com scrypt para password com salt salt em formato PHC:
$scrypt$ln=12,r=4,p=1$c2FsdA$mavmarxlZ75BgquH1bbcFg
Este valor contém os seguintes componentes codificados:
$scrypt$: Identificador do algoritmo.
ln=12,r=4,p=1: Parâmetros de custo:
ln=12 significa 2^12 = 4096 como factor N.
r=4 é o bloco de memória.
p=1 é o grau de paralelismo.
c2FsdA: Salt codificado em Base64.
mavmarxlZ75BgquH1bbcFg: valor de dispersão derivado codificado em Base64.
Este formato permite armazenar, num único campo, todos os elementos necessários para verificar a palavra-passe: algoritmo, parâmetros, salt e o valor de dispersão derivado. Ao verificar uma palavra-passe, o sistema extrai os parâmetros e o salt, aplica o scrypt e compara o resultado com o valor de dispersão armazenado, idealmente usando comparação em tempo constante.
Nota: Este exemplo é compacto e legível, útil para documentação. Parâmetros tão baixos não devem ser usados em produção. Um sistema real usaria pelo menos ln=14, r=8, e um salt de 128 bits com entropia suficiente.
A correta parametrização destes valores permite dimensionar o esforço necessário para gerar cada valor de dispersão, tornando ataques por tentativa computacional e economicamente mais dispendiosos.
O scrypt continua a ser uma opção segura e eficaz em ambientes onde a resistência à paralelização com hardware especializado é um requisito essencial. Contudo, a sua complexidade de configuração e menor suporte nativo em algumas bibliotecas e plataformas tornam-no menos comum em implementações modernas.
Ainda assim, quando devidamente parametrizado, o scrypt oferece um equilíbrio robusto entre segurança, desempenho e resistência prática a ataques dirigidos a sistemas de armazenamento de palavras-passe.
> Argon2
O Argon2 foi desenhado para substituir mecanismos anteriores como bcrypt, PBKDF2 e scrypt, com o objetivo de oferecer maior resistência a ataques em hardware especializado. Foi o vencedor do Password Hashing Competition (PHC) em 2015, sendo desde então considerado um padrão moderno e recomendado para armazenamento seguro de palavras-passe.
O Argon2 apresenta-se em três variantes:
Argon2d: utiliza endereçamento de memória data-dependent, maximizando a resistência a ataques por tentativa paralelizados, mas mais vulneravel a ataques por canal lateral devido ao acesso não determinístico.
Argon2i: usa endereçamento data-independent, mitigando ataques por canal lateral através de padrões de acesso previsíveis, embora com menor eficácia contra implementações paralelas.
Argon2id: combina as abordagens anteriores, sendo a opção recomendada para a maioria dos cenários práticos.
Tal como o scrypt, o Argon2 permite controlar três parâmetros principais: A quantidade de memória a ser utilizada durante a derivação, o número de iterações e o número de threads ou unidades lógicas a serem usadas em paralelo.
Exemplo de valor derivado com Argon2id para password com salt bad-salt em formato PHC:
$argon2id$v=19$m=65536,t=3,p=4$YmFkLXNhbHQ$eynVZx4VREpVVM8qx9UbjFwBWC1J6WJF5TLh7jFWE+k
Este valor contém os seguintes componentes:
$argon2id$: identificador do algoritmo.
v=19: versão da função.
m=65536: 64 MB de memória.
t=3: três iterações.
p=4: quatro graus de paralelismo.
YmFkLXNhbHQ: salt codificado em Base64.
eynVZx4VREpVVM8qx9UbjFwBWC1J6WJF5TLh7jFWE+k: valor de dispersão derivado, também em Base64.
Nota: Em contextos reais, o comprimento do valor de dispersão e do salt deve ser mais elevado para assegurar resistência a ataques.
Este formato, tal como no scrypt, permite armazenar todos os elementos necessários para a verificação da palavra-passe. Na validação, o sistema extrai os parâmetros e o salt, aplica o Argon2id e compara o resultado com o valor armazenado, preferencialmente usando comparação em tempo constante.
Argon2id é atualmente considerado a escolha mais segura e equilibrada para armazenamento de palavras-passe. A sua resistência à paralelização e consumo de memória configurável tornam-no especialmente eficaz contra ataques em hardware especializado. Contudo, requer bibliotecas modernas para implementação correta, o que pode limitar o suporte nativo em sistemas mais antigos.
Quando bem parametrizado e usado com salts únicos por utilizador, o Argon2id oferece um dos melhores níveis de proteção disponíveis para armazenamento de palavras-passe em sistemas contemporâneos.
> pepper
O peppering é uma técnica adicional de proteção de palavras-passe que consiste na utilização de um valor secreto partilhado, designado por pepper, aplicado ao resultado do processo de dispersão. O objetivo é dificultar o trabalho de um atacante que consiga aceder à base de dados, mas não tenha acesso ao segredo utilizado.
Ao contrário do salt, que é único por palavra-passe e armazenado com o valor de dispersão, o pepper é comum a todas as entradas e nunca deve ser guardado na base de dados. Deve ser tratado como um segredo criptográfico e mantido num repositório seguro, como um HSM ou um cofre de segredos devidamente gerido.
Uma estratégia comum é aplicar um algoritmo adequado à palavra-passe, por exemplo, Argon2, bcrypt ou scrypt e, em seguida, aplicar uma função HMAC ao resultado, usando o pepper como chave. Apenas o resultado final é armazenado.
O pepper deve ser sujeito a políticas de segurança rigorosas, incluindo rotação periódica, tal como qualquer outra chave sensível. Esta abordagem não altera a função de dispersão criptográfica utilizada, mas adiciona uma camada adicional de proteção caso o sistema de armazenamento seja comprometido.
> conclusão
A segurança no armazenamento de palavras-passe representa uma componente crítica da proteção dos sistemas modernos de autenticação. A utilização de funções de dispersão criptográfica genéricas, concebidas para rapidez e eficiência, revela-se manifestamente insuficiente face às ameaças atuais. A escolha de algoritmos especificamente desenhados para este fim é essencial para garantir resistência prática a ataques por exaustão e tentativas de quebra em larga escala.
Técnicas como o salting introduzem variabilidade e evitam padrões previsíveis que facilitariam ataques com tabelas pré-computadas. A aplicação de funções como PBKDF2, bcrypt, scrypt e, idealmente, Argon2id, permite aumentar o custo computacional e limitar significativamente a eficácia de hardware dedicado à quebra de credenciais.
A introdução de um pepper acrescenta uma camada extra de proteção ao processo de armazenamento, dificultando significativamente a tarefa de um atacante que tenha acesso apenas à base de dados, ao exigir também o acesso a um segredo externo devidamente protegido.
Cada uma destas funções oferece um conjunto distinto de características e deve ser selecionada em função do contexto de utilização, da sensibilidade dos dados protegidos e das capacidades técnicas do sistema.
Importa sublinhar que a eficácia de qualquer técnica depende não apenas da tecnologia empregue, mas do rigor na gestão de chaves, da proteção dos canais auxiliares como cópias de segurança ou registos e da observância de boas práticas de desenvolvimento.
O armazenamento seguro de palavras-passe exige uma abordagem informada, rigorosa e continuamente atualizada, integrando-se numa estratégia global de defesa em profundidade e mitigação de risco. Em última análise, o compromisso com a segurança das credenciais é um reflexo direto do compromisso com a segurança e confiança dos utilizadores.
> status: secured
> exit 0