2014-12-17 19 views
23

Ho implementato un BloomFilter in python 3.3 e ottenuto risultati diversi ogni sessione. Esaminare questo comportamento strano mi ha portato alla funzione interna di hash() - restituisce diversi valori hash per la stessa stringa in ogni sessione.in Python 3.3 restituisce risultati diversi tra le sessioni

Esempio:

>>> hash("235") 
-310569535015251310 

----- l'apertura di una nuova console pitone -----

>>> hash("235") 
-1900164331622581997 

Perché succede questo? Perché è utile?

+2

Questa è una funzione di sicurezza. –

+0

Contrassegnato [tag: collisione hash], [tag: sicurezza], [tag: python-3.3] – smci

risposta

38

Python utilizza un seme di hash casuale per impedire agli attaccanti di tar-pitting dell'applicazione inviando chiavi progettate per scontrarsi. Vedi lo original vulnerability disclosure. Sfogliando l'hash con un seme casuale (impostato una volta all'avvio), gli attaccanti non possono più prevedere quali tasti entreranno in collisione.

È possibile impostare un seme fisso o disabilitare la funzione impostando PYTHONHASHSEED environment variable; il valore predefinito è random ma è possibile impostarlo su un valore intero positivo fisso, con 0 disattivando del tutto la funzione.

Le versioni Python 2.7 e 3.2 sono disabilitate per impostazione predefinita (utilizzare lo switch -R o impostare PYTHONHASHSEED=random per abilitarlo); è abilitato di default in Python 3.3 e versioni successive.

Se si stava facendo affidamento sull'ordine delle chiavi in ​​un dizionario Python o in un set, non farlo. Python usa una tabella hash per implementare questi tipi e il loro ordine depends on the insertion and deletion history così come i semi hash casuali.

vedi anche la object.__hash__() special method documentation:

Nota: Per default, i valori di __hash__() str, byte e oggetti datetime vengono “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 peggiori prestazioni di un inserimento dict, complessità O (n^2). Vedere http://www.ocert.org/advisories/ocert-2011-003.html per i dettagli.
La modifica dei valori hash influisce sull'ordine di ripetizione di dicts, set e altri mapping. Python non ha mai fornito garanzie su questo ordine (e varia tipicamente tra i build a 32 e 64 bit).
Vedere anche PYTHONHASHSEED.

Se è necessaria un'implementazione di hash stabile, probabilmente si desidera esaminare hashlib module; questo implementa le funzioni hash crittografiche. Il pybloom project uses this approach.

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

+0

Mi aspetto che questo venga visualizzato nei documenti hash() e non solo in __hash __(). +1 per un'ottima risposta. p.s. L'hashlib non è un overkill per usi non crittografici di funzioni hash? – redlus

+0

pybloom utilizza le funzioni hashlib. Ma se vuoi qualcosa di più veloce, puoi controllare [pyhash] (https://github.com/flier/pyfasthash). –

+0

Perché la documentazione lo chiama 'disable' quando lo si imposta su 0? Non vedo l'effettiva differenza di impostarlo su un numero di seme stabile precedente, a meno che non mi manchi qualcosa. Ciò che intendo è quando uso 'PYTHONHASHSEED = 12345' Ottengo lo stesso hash per stringhe uguali anche attraverso le sessioni - lo stesso accade quando uso' PYTHONHASHSEED = 0' - l'hash per stringhe uguali sarà lo stesso per tutte le sessioni (anche se diverso a 12345, ma questo è ovvio, è così che funzionano i semi). – blubberdiblub

3

Hash randomisation è turned on by default in Python 3.Questa è una caratteristica di sicurezza:

Hash randomizzazione è destinato a fornire una protezione contro una negazione del servizio causati da ingressi accuratamente scelti che sfruttano l'andamento peggiore caso di una costruzione dict

Nel precedente versioni da 2.6.8, è possibile accenderlo alla riga di comando con -R, o l'opzione di ambiente PYTHONHASHSEED.

È possibile disattivarlo impostando PYTHONHASHSEED su zero.

+0

Questo spiega solo come disabilitare la funzione, non perché è lì in primo luogo. –

+1

@MartijnPieters Non ho avuto il tempo di espandere la mia risposta come hai fatto tu. –

-2

hash() è un Python funzione built-in e usarlo per calcolare un valore hash per oggetto, non per stringa o num.

È possibile visualizzare 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 dottore dice i seguenti:

Per default, i valori hash () di str, byte e oggetti datetime vengono “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 di hash diverso per la stessa stringa in console diverse.

Ciò che implementate non è un buon modo.

Quando si vuole calcolare un valore stringa di hash, basta usare hashlib

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

+3

'hash()' è perfettamente valido per valori stringa o numerici. Stai confondendo questo con il metodo personalizzato '__hash__', usato ** da' hash() '** per fornire un'implementazione personalizzata del valore hash. –