La funzione hash in Python 3.3 restituisce risultati diversi tra le sessioni


100

Ho implementato un BloomFilter in Python 3.3 e ho ottenuto risultati diversi ad ogni sessione. Analizzare questo strano comportamento mi ha portato alla funzione hash () interna: restituisce valori hash diversi per la stessa stringa ogni sessione.

Esempio:

>>> hash("235")
-310569535015251310

----- apertura di una nuova console python -----

>>> hash("235")
-1900164331622581997

Perché sta succedendo? Perché è utile?

Risposte:


136

Python utilizza un seme hash casuale per impedire agli aggressori di eseguire il tar-pitting della tua applicazione inviandoti chiavi progettate per collidere. Vedere l' informativa sulla vulnerabilità originale . Compensando l'hash con un seme casuale (impostato una volta all'avvio) gli aggressori non possono più prevedere quali chiavi entreranno in collisione.

È possibile impostare un seme fisso o disabilitare la funzionalità impostando la PYTHONHASHSEEDvariabile d'ambiente ; il valore predefinito è randomma puoi impostarlo su un valore intero positivo fisso, 0disabilitando completamente la funzione.

Le versioni 2.7 e 3.2 di Python hanno la funzione disabilitata per impostazione predefinita (usa l' -Rinterruttore o impostalo PYTHONHASHSEED=randomper abilitarlo); è abilitato per impostazione predefinita in Python 3.3 e versioni successive.

Se ti affidi all'ordine delle chiavi in ​​un set Python, allora non farlo. Python utilizza una tabella hash per implementare questi tipi e il loro ordine dipende dalla cronologia di inserimento ed eliminazione , nonché dal seme di hash casuale. Nota che in Python 3.5 e versioni precedenti, questo vale anche per i dizionari.

Vedere anche la object.__hash__()documentazione del metodo speciale :

Nota : per impostazione predefinita, i __hash__()valori di str, byte e oggetti datetime sono "salati" con un valore casuale imprevedibile. Sebbene rimangano costanti all'interno di un singolo processo Python, non sono prevedibili tra invocazioni ripetute di Python.

Questo ha lo scopo di fornire protezione contro un denial-of-service causato da input scelti con cura che sfruttano le prestazioni nel caso peggiore di un inserimento di dict, O (n ^ 2) complessità. Vedi http://www.ocert.org/advisories/ocert-2011-003.html per i dettagli.

La modifica dei valori hash influisce sull'ordine di iterazione di dict, set e altre mappature. Python non ha mai fornito garanzie su questo ordine (e in genere varia tra build a 32 bit e 64 bit).

Vedi anche PYTHONHASHSEED.

Se hai bisogno di un'implementazione hash stabile, probabilmente vorrai guardare il hashlibmodulo ; questo implementa le funzioni hash crittografiche. Il progetto pybloom utilizza questo approccio .

Poiché l'offset è costituito da un prefisso e un suffisso (valore iniziale e valore XORed finale, rispettivamente) non è possibile memorizzare l'offset, sfortunatamente. Tra i lati positivi, ciò significa che gli attaccanti non possono facilmente determinare l'offset con gli attacchi a tempo.


9
Mi aspetto che questo venga visualizzato nei documenti hash () e non solo in __hash __ (). +1 per un'ottima risposta. ps hashlib non è eccessivo per gli usi non crittografici delle funzioni hash?
redlus

1
pybloom utilizza le funzioni hashlib. Ma se vuoi qualcosa di più veloce, potresti dare un'occhiata a pyhash .
Håken Lid

3
Perché la documentazione lo chiama disablequando lo imposta a 0? Non vedo l'effettiva differenza nell'impostarlo su un vecchio numero seed stabile, a meno che non mi manchi qualcosa. Quello che voglio dire è che quando uso PYTHONHASHSEED=12345ottengo lo stesso hash per stringhe uguali anche tra sessioni - lo stesso accade quando uso PYTHONHASHSEED=0- l'hash per stringhe uguali sarà lo stesso per tutte le sessioni (anche se diverso da 12345, ma è ovvio, è così che i semi lavoro).
blubberdiblub

@blubberdiblub: 0senza alcun seed e gli hash per gli oggetti sono uguali a quelli generati in una versione precedente di Python senza alcun supporto per hashseed.
Martijn Pieters

1
@MartijnPieters cosa significa che gli hash interessati non hanno "alcun seme"? Qual è la differenza semantica o qualitativa nell'avere un seme di, diciamo, 12345, a parte il fatto che crea due serie distinte di sessioni tra le quali i valori hash sono diversi e oltre al fatto che PYTHONHASHSEED = 0 è uguale alle versioni precedenti? Puoi collegarmi a un particolare pezzo di codice sorgente? Immagino che il mio punto sia che se non ci fosse tale differenza, lo chiamerei seme di 0 e versioni precedenti di Python che supportano solo un seme di 0. La documentazione così com'è adesso è abbastanza confusa per me.
blubberdiblub

10

La randomizzazione degli hash è attivata per impostazione predefinita in Python 3 . Questa è una funzione di sicurezza:

La randomizzazione degli hash ha lo scopo di fornire protezione contro un denial-of-service causato da input scelti con cura che sfruttano le prestazioni del caso peggiore di una costruzione di dict

Nelle versioni precedenti dalla 2.6.8, era possibile attivarlo dalla riga di comando con -R o con l' opzione dell'ambiente PYTHONHASHSEED .

È possibile spegnerlo impostando PYTHONHASHSEEDa zero.


-9

hash () è una funzione incorporata di Python e la usa per calcolare un valore hash per oggetto , non per stringa o num.

Puoi vedere i dettagli in questa pagina: https://docs.python.org/3.3/library/functions.html#hash .

e i valori hash () provengono dal metodo __hash__ dell'oggetto. Il doc dice quanto segue:

Per impostazione predefinita, i valori hash () di str, byte e oggetti datetime sono "salati" con un valore casuale imprevedibile. Sebbene rimangano costanti all'interno di un singolo processo Python, non sono prevedibili tra invocazioni ripetute di Python.

Ecco perché hai un valore hash diverso per la stessa stringa in console diverse.

Quello che implementi non è un buon modo.

Quando vuoi calcolare un valore hash di stringa, usa semplicemente hashlib

hash () ha lo scopo di ottenere un valore hash di un oggetto, non uno stirng.


6
hash()è perfettamente valido per stringhe o valori numerici. Lo stai confondendo con il __hash__metodo personalizzato, utilizzato dahash() per fornire un'implementazione personalizzata del valore hash.
Martijn Pieters
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.