Uma função de hash criptográfica é uma das ferramentas mais discretas e mais importantes da segurança informática. Recebe dados de qualquer tamanho e devolve uma sequência curta e de comprimento fixo, a que se chama resumo, ou digest. Esse resumo funciona como uma impressão digital dos dados: identifica-os de forma fiável sem revelar nem permitir reconstruir o conteúdo original. Este artigo explica as propriedades que definem uma boa função de hash, como difere de um hash comum e onde se usa na prática.

A ideia central: uma impressão digital de comprimento fixo

A operação básica de uma função de hash é simples de descrever. Entra um conjunto de dados, que tanto pode ser uma palavra como um ficheiro inteiro, e sai uma cadeia de bytes de tamanho sempre igual, normalmente apresentada em hexadecimal. O SHA-256, por exemplo, devolve sempre 256 bits, ou seja, 64 caracteres hexadecimais, qualquer que seja a entrada.

O que torna uma função de hash útil para a segurança não é apenas produzir esse resumo, mas as garantias que oferece sobre a relação entre entradas e saídas. São essas garantias, e não a operação em si, que distinguem uma função criptográfica de um cálculo qualquer.

As propriedades que definem uma função de hash criptográfica

Há um conjunto de propriedades que uma função de hash precisa de cumprir para ser considerada criptograficamente segura. Falhar qualquer uma delas costuma ser suficiente para a desqualificar de usos sensíveis.

Determinística

A mesma entrada produz sempre o mesmo resumo, em qualquer máquina e em qualquer momento. Sem esta propriedade, um hash não serviria como identificador estável, porque o mesmo ficheiro poderia gerar valores diferentes em ocasiões diferentes.

Rápida de calcular

Calcular o resumo de uma entrada deve ser eficiente, mesmo para ficheiros grandes. Isto permite verificar integridade, indexar dados ou comparar conteúdos sem um custo proibitivo. Há, porém, uma exceção importante: para guardar palavras-passe quer-se exatamente o contrário, uma função deliberadamente lenta, como se verá adiante.

Resistência à pré-imagem

A partir de um resumo, deve ser inviável encontrar uma entrada que o produza. O cálculo é fácil da entrada para a saída e impraticável no sentido inverso. É esta propriedade que torna o hashing um processo de sentido único e que permite guardar resumos em vez dos dados originais.

Resistência à segunda pré-imagem

Dada uma entrada concreta, deve ser inviável encontrar uma entrada diferente com o mesmo resumo. Por outras palavras, conhecendo um ficheiro e o seu hash, ninguém deve conseguir construir um segundo ficheiro que produza precisamente aquele resumo. Esta é a garantia que protege uma verificação de integridade sobre um documento específico.

Resistência a colisões

Deve ser inviável encontrar duas entradas quaisquer, livremente escolhidas, com o mesmo resumo. A diferença em relação à segunda pré-imagem é subtil mas decisiva: aqui o atacante pode escolher ambos os ficheiros à vontade. Como as saídas têm tamanho fixo e as entradas são ilimitadas, as colisões existem por necessidade matemática. A promessa de segurança é apenas que ninguém as consiga encontrar dentro de um tempo de cálculo razoável. Foi precisamente esta propriedade que ruiu no SHA-1, como se descreve no artigo sobre a colisão de SHA-1.

Efeito de avalanche

Mudar um único bit da entrada altera cerca de metade dos bits da saída. O novo resumo parece não ter qualquer relação com o anterior. Esta propriedade garante que o hash não deixa transparecer o grau de semelhança entre duas entradas e reforça, na prática, todas as outras resistências.

Como diferem de um hash não criptográfico

Nem todas as funções de hash são criptográficas, e confundir os dois tipos é uma fonte comum de erros de segurança. Existem funções de hash de uso geral, como as usadas em tabelas de dispersão dentro de programas, ou somas de verificação como o CRC32, concebidas para detetar corrupção acidental de dados.

Estas funções não criptográficas são desenhadas para velocidade e boa distribuição, não para resistir a um adversário. É perfeitamente fácil, e até esperado, encontrar colisões num CRC32. Servem bem para repartir dados por uma tabela ou para apanhar um erro de transmissão aleatório, mas não oferecem qualquer garantia contra alguém que tente, de propósito, forjar uma correspondência.

A distinção prática é esta: uma função criptográfica assume a existência de um atacante ativo, com recursos, a tentar quebrá-la, e mantém-se de pé mesmo assim. Uma função não criptográfica assume apenas ruído e acidente. Usar uma soma de verificação simples onde se precisa de segurança é um dos enganos mais perigosos nesta área.

Usos comuns na prática

As funções de hash criptográficas aparecem em muitos pontos da computação moderna, quase sempre sem o utilizador dar por isso.

Armazenamento de palavras-passe com sal

Os serviços bem desenhados nunca guardam a palavra-passe em claro. Guardam o seu hash, de modo que uma fuga da base de dados não entregue as credenciais reais. A isto acrescenta-se um valor aleatório único por utilizador, chamado sal, que se junta à palavra-passe antes de calcular o resumo. O sal garante que duas pessoas com a mesma palavra-passe acabam com hashes diferentes e inutiliza tabelas de resumos pré-calculados. Para este uso específico, em vez de um hash rápido, prefere-se uma função propositadamente lenta, como bcrypt, scrypt ou Argon2, que torna cada tentativa de adivinhação cara para o atacante.

Verificações de integridade

Publicar o resumo de um ficheiro ao lado do próprio ficheiro permite a qualquer pessoa confirmar que o descarregamento chegou intacto. Calcula-se o hash localmente e compara-se com o valor publicado. Se coincidem, os dados não mudaram; se não, foram corrompidos ou trocados. É a forma mais leve de verificar integridade.

Assinaturas digitais

As assinaturas digitais não assinam o documento inteiro, mas o seu resumo. O hash reduz a mensagem a um valor de tamanho fixo, e a assinatura, feita com uma chave privada, cobre esse valor. Esta combinação de hashing com chaves assimétricas está descrita no artigo sobre assinaturas digitais, e é também a razão pela qual uma colisão de hash compromete diretamente a segurança das assinaturas.

Blockchains

As blockchains encadeiam blocos referenciando, em cada bloco, o hash do anterior. Qualquer alteração num bloco antigo mudaria o seu resumo e quebraria toda a cadeia que se segue, o que torna o histórico resistente a adulteração. A Bitcoin usa o SHA-256 tanto para encadear os blocos como para a sua prova de trabalho.

Escolher a função certa

Nem todas as funções de hash são equivalentes, e a escolha depende do estado de segurança de cada uma. O MD5 e o SHA-1 estão quebrados quanto à resistência a colisões e não devem ser usados onde isso importe. O SHA-256, da família SHA-2, é a escolha segura por defeito para a generalidade dos fins. O SHA-3 oferece uma alternativa sólida assente noutra estrutura interna. E, para palavras-passe, a regra é diferente de tudo o resto: usa-se uma função lenta e com sal, desenhada para esse propósito.

Para situar estas funções no conjunto maior das ferramentas criptográficas, e perceber como se relacionam com a cifragem e as assinaturas, veja o artigo central de criptografia.

Perguntas frequentes

Qual a diferença entre resistência à segunda pré-imagem e resistência a colisões?

Na segunda pré-imagem, o ficheiro de partida está fixado e o atacante tem de encontrar um segundo ficheiro com o mesmo resumo. Na resistência a colisões, o atacante é livre de escolher ambos os ficheiros. Encontrar uma colisão é, por isso, mais fácil para o atacante, razão pela qual costuma ser a primeira propriedade a cair, como aconteceu com o SHA-1.

Posso usar MD5 para alguma coisa hoje?

Apenas como soma de verificação simples, para detetar corrupção acidental, e nunca onde a segurança esteja em jogo. As colisões de MD5 geram-se em segundos, pelo que qualquer uso em que um atacante possa lucrar com uma correspondência forjada está fora de questão.

Uma função de hash criptográfica cifra os meus dados?

Não. Hashing e cifragem são coisas distintas. A cifragem é reversível com uma chave e destina-se a recuperar a mensagem mais tarde. Uma função de hash é de sentido único e produz uma impressão digital que nunca se pretende inverter.