2014-12-17 43 views
23

Zaimplementowałam BloomFilter w pythonie 3.3 i uzyskałem różne wyniki w każdej sesji. Prześledzenie tego dziwnego zachowania doprowadziło mnie do wewnętrznej funkcji hash() - zwraca ona różne wartości mieszania dla tego samego ciągu znaków w każdej sesji.Funkcja skrótu w Pythonie 3.3 zwraca różne wyniki między sesjami

Przykład:

>>> hash("235") 
-310569535015251310 

----- otwarcie nowej konsoli Pythona -----

>>> hash("235") 
-1900164331622581997 

Dlaczego tak się dzieje? Dlaczego jest to przydatne?

+2

To jest funkcja zabezpieczająca. –

+0

Tagged [tag: hash-collision], [tag: security], [tag: python-3.3] – smci

Odpowiedz

38

Python używa losowego ziarna mieszającego, aby uniemożliwić napastnikowi atakowanie aplikacji przez wysyłanie kluczy przeznaczonych do zderzenia. Zobacz original vulnerability disclosure. Przesunięcie wartości mieszania o losowy materiał siewny (ustawione podczas uruchamiania raz) nie może już przewidzieć, jakie klucze będą kolidować.

Możesz ustawić stały materiał siewny lub wyłączyć tę funkcję, ustawiając PYTHONHASHSEED environment variable; wartością domyślną jest random, ale można ustawić ją na stałą dodatnią wartość całkowitą, przy czym 0 całkowicie wyłącza tę funkcję.

Wersje w języku Python w wersjach 2.7 i 3.2 mają domyślnie wyłączoną funkcję (użyj przełącznika -R lub ustaw PYTHONHASHSEED=random, aby go włączyć); jest on domyślnie włączony w Pythonie 3.3 i wyżej.

Jeśli polegałeś na kolejności klawiszy w słowniku lub zestawie Pythona, nie rób tego. Python używa tabeli mieszania, aby zaimplementować te typy i ich kolejność: depends on the insertion and deletion history, a także losowe ziarno mieszające.

Zobacz także object.__hash__() special method documentation:

Uwaga: domyślnie wartości __hash__() STW, bajtów i obiektów datetime są „solone” z nieprzewidywalnym wartości losowej. Mimo że pozostają one stałe w ramach pojedynczego procesu Pythona, nie można ich przewidzieć między powtórnymi wywołaniami Pythona.
Ma to na celu zapewnienie ochrony przed odmową usługi spowodowaną ostrożnie wybranymi danymi wejściowymi, które wykorzystują najgorszy przypadek wykonania wstawienia dyktowanego, złożoność O (n^2). Aby uzyskać szczegółowe informacje, patrz http://www.ocert.org/advisories/ocert-2011-003.html.
Zmiana wartości mieszania wpływa na kolejność iteracji dykt, zestawów i innych mapowań. Python nigdy nie udzielił gwarancji na temat tego zamawiania (zazwyczaj różni się między wersjami 32-bitowymi i 64-bitowymi).
Zobacz także PYTHONHASHSEED.

Jeśli potrzebujesz stabilnej implementacji skrótu, prawdopodobnie chcesz spojrzeć na hashlib module; implementuje kryptograficzne funkcje skrótu. The pybloom project uses this approach.

Ponieważ przesunięcie składa się z przedrostka i sufiksu (odpowiednio wartość początkowa i końcowa XOR), nie można po prostu zapisać przesunięcia. Z drugiej strony oznacza to, że atakujący nie mogą łatwo określić przesunięcia z atakami taktowania.

+0

Spodziewam się, że to pojawi się w dokumentach hash(), a nie tylko w __hash __(). +1 za wspaniałą odpowiedź. p.s. Czy hashlib nie jest przesadą dla niekryptograficznych zastosowań funkcji haszujących? – redlus

+0

pybloom używa funkcji hashlib. Ale jeśli chcesz czegoś szybciej, możesz sprawdzić [pyhash] (https://github.com/flier/pyfasthash). –

+0

Dlaczego dokumentacja nazywa to 'wyłączeniem' przy ustawieniu na 0? Nie widzę żadnej różnicy w ustawianiu go na żaden stary stabilny numer początkowy, chyba że czegoś mi brakuje. Mam na myśli to, że kiedy używam 'PYTHONHASHSEED = 12345' otrzymuję ten sam skrót dla równych łańcuchów nawet pomiędzy sesjami - to samo dzieje się, gdy używam' PYTHONHASHSEED = 0' - mieszanie dla równych łańcuchów będzie takie samo w różnych sesjach (choć różne do 12345, ale to oczywiste, tak działają nasiona). – blubberdiblub

3

Hash randomizacja to turned on by default in Python 3.Jest to funkcja zabezpieczeń:

Hash randomizacji ma na celu zapewnienie ochrony przed zaprzeczeniem-of-service spowodowane przez starannie wybranych wejść, które wykorzystują najgorszą wydajność przypadek konstrukcji dict

w poprzednich wersje od wersji 2.6.8, można je włączyć w wierszu poleceń za pomocą opcji -R lub PYTHONHASHSEED.

Można go wyłączyć, ustawiając wartość PYTHONHASHSEED na zero.

+0

Wyjaśnia to tylko, jak wyłączyć tę funkcję, a nie dlaczego jest dostępna. –

+1

@MartijnPieters Nie poświęciłem czasu, aby rozszerzyć moją odpowiedź tak jak ty. –

-2

hash() jest Python wbudowana funkcja i użyć go do obliczania wartości hash dla obiektu, nie do sznurka lub num.

Możesz zobaczyć szczegóły na tej stronie: https://docs.python.org/3.3/library/functions.html#hash.

Wartości

i hash() pochodzą z metody obiektu __hash__. Doc mówi następujących:

domyślnie hash() Wartości Str, bajtów i obiektów datetime są „solone” z nieprzewidywalnym wartości losowej. Mimo że pozostają one stałe w ramach pojedynczego procesu Pythona, nie można ich przewidzieć między powtórnymi wywołaniami Pythona.

Dlatego Twoja wartość diffent hash dla tego samego ciągu w innej konsoli.

To, co stosujesz, nie jest dobrą metodą.

Gdy chcesz obliczyć wartość hash ciąg, wystarczy użyć hashlib

hash() jest dążyć do uzyskania wartości hash obiektu, a nie stirng.

+3

'hash()' jest całkowicie poprawne dla wartości łańcuchowych lub liczbowych. Mylicie to za pomocą niestandardowej metody '__hash__', używanej ** przez' hash() '** w celu zapewnienia niestandardowej implementacji wartości skrótu. –