A função hash no Python 3.3 retorna resultados diferentes entre as sessões


97

Implementei um BloomFilter no python 3.3 e obtive resultados diferentes a cada sessão. Detalhar esse comportamento estranho me levou à função hash () interna - ela retorna valores de hash diferentes para a mesma string a cada sessão.

Exemplo:

>>> hash("235")
-310569535015251310

----- abrindo um novo console Python -----

>>> hash("235")
-1900164331622581997

Por que isso está acontecendo? Por que isso é útil?

Respostas:


136

Python usa uma semente de hash aleatória para evitar que invasores ataquem seu aplicativo enviando chaves projetadas para colidir. Veja a divulgação original da vulnerabilidade . Ao compensar o hash com uma semente aleatória (definida uma vez na inicialização), os invasores não podem mais prever quais chaves entrarão em conflito.

Você pode definir uma semente fixa ou desativar o recurso definindo a PYTHONHASHSEEDvariável de ambiente ; o padrão é, randommas você pode defini-lo como um valor inteiro positivo fixo, com a 0desativação do recurso por completo.

As versões 2.7 e 3.2 do Python têm o recurso desabilitado por padrão (use a -Ropção ou defina PYTHONHASHSEED=randompara habilitá-lo); ele é habilitado por padrão no Python 3.3 e superior.

Se você estava contando com a ordem das chaves em um conjunto Python, não o faça. Python usa uma tabela de hash para implementar esses tipos e sua ordem depende do histórico de inserção e exclusão , bem como da semente de hash aleatória. Observe que no Python 3.5 e anteriores, isso também se aplica aos dicionários.

Veja também a object.__hash__()documentação do método especial :

Nota : Por padrão, os __hash__()valores dos objetos str, bytes e datetime são “salgados” com um valor aleatório imprevisível. Embora permaneçam constantes em um processo individual do Python, eles não são previsíveis entre invocações repetidas do Python.

Isso se destina a fornecer proteção contra uma negação de serviço causada por entradas cuidadosamente escolhidas que exploram o desempenho de pior caso de uma inserção de dicionário, complexidade O (n ^ 2). Consulte http://www.ocert.org/advisories/ocert-2011-003.html para obter detalhes.

Alterar os valores de hash afeta a ordem de iteração de dictos, conjuntos e outros mapeamentos. Python nunca deu garantias sobre essa ordem (e normalmente varia entre compilações de 32 e 64 bits).

Veja também PYTHONHASHSEED.

Se você precisar de uma implementação de hash estável, provavelmente desejará examinar o hashlibmódulo ; isso implementa funções de hash criptográficas. O projeto pybloom usa essa abordagem .

Como o deslocamento consiste em um prefixo e um sufixo (valor inicial e valor final XORed, respectivamente), você não pode simplesmente armazenar o deslocamento, infelizmente. No lado positivo, isso significa que os invasores também não podem determinar facilmente o deslocamento com ataques temporizados.


9
Eu espero que isso apareça nos documentos hash () e não apenas em __hash __ (). 1 para uma ótima resposta. ps Não é hashlib um exagero para usos não criptográficos de funções hash?
redlus

1
pybloom usa as funções hashlib. Mas se você quiser algo mais rápido, pode verificar o pyhash .
Håken Lid

3
Por que a documentação o chama disableao definir como 0? Não vejo diferença efetiva em configurá-lo para qualquer número de semente estável antigo, a menos que esteja faltando alguma coisa. O que quero dizer é que, quando eu uso PYTHONHASHSEED=12345, obtenho o mesmo hash para strings iguais, mesmo nas sessões - o mesmo acontece quando eu uso PYTHONHASHSEED=0- o hash para strings iguais será o mesmo entre as sessões (embora seja diferente de 12345, mas isso é óbvio, é assim que as sementes trabalhos).
blubberdiblub

@blubberdiblub: com 0não há nenhuma semente e os hashes para objetos são iguais aos gerados em uma versão mais antiga do Python sem qualquer suporte para hashseed.
Martijn Pieters

1
@MartijnPieters o que significa para os hashes afetados "nenhuma semente"? Qual é a diferença semântica ou qualitativa em ter uma semente de, digamos, 12345, além do fato de que isso cria dois conjuntos distintos de sessões entre os quais os valores de hash são diferentes e além de PYTHONHASHSEED = 0 ser igual a versões mais antigas? Você pode me vincular a um determinado código-fonte? Acho que meu ponto é que, se não houver essa diferença, eu o chamaria de seed of 0 e versões mais antigas do Python suportando apenas seed de 0. A documentação como está agora é bastante confusa para mim.
blubberdiblub de

10

A randomização de hash é ativada por padrão no Python 3 . Este é um recurso de segurança:

A randomização de hash destina-se a fornecer proteção contra uma negação de serviço causada por entradas cuidadosamente escolhidas que exploram o pior caso de desempenho de uma construção de dicionário

Em versões anteriores de 2.6.8, você poderia ativá- lo na linha de comando com -R ou a opção de ambiente PYTHONHASHSEED .

Você pode desligá-lo definindo PYTHONHASHSEEDcomo zero.


-9

hash () é uma função integrada do Python e usa-a para calcular um valor de hash para o objeto , não para string ou num.

Você pode ver os detalhes nesta página: https://docs.python.org/3.3/library/functions.html#hash .

e os valores hash () vêm do método __hash__ do objeto. O doc diz o seguinte:

Por padrão, os valores hash () de objetos str, bytes e datetime são “salgados” com um valor aleatório imprevisível. Embora permaneçam constantes em um processo individual do Python, eles não são previsíveis entre invocações repetidas do Python.

É por isso que você tem um valor hash diferente para a mesma string em um console diferente.

O que você implementa não é uma boa maneira.

Quando você quiser calcular um valor de hash de string, basta usar hashlib

hash () tem como objetivo obter um valor de hash de objeto, não uma agitação.


6
hash()é perfeitamente válido para string ou valores numéricos. Você está confundindo isso com o __hash__método personalizado, usado porhash() para fornecer uma implementação personalizada do valor hash.
Martijn Pieters
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.