La función hash en Python 3.3 devuelve diferentes resultados entre sesiones

100

Implementé un BloomFilter en Python 3.3 y obtuve resultados diferentes en cada sesión. Profundizar en este comportamiento extraño me llevó a la función interna hash (): devuelve diferentes valores hash para la misma cadena en cada sesión.

Ejemplo:

>>> hash("235")
-310569535015251310

----- abriendo una nueva consola de Python -----

>>> hash("235")
-1900164331622581997

¿Por qué está pasando esto? ¿Por qué es útil esto?

redlus
fuente

Respuestas:

136

Python usa una semilla de hash aleatoria para evitar que los atacantes pongan alquitrán en su aplicación enviándole claves diseñadas para colisionar. Ver la divulgación de vulnerabilidad original . Al compensar el hash con una semilla aleatoria (establecida una vez al inicio), los atacantes ya no pueden predecir qué claves colisionarán.

Puede establecer una semilla fija o deshabilitar la función configurando la PYTHONHASHSEEDvariable de entorno ; el valor predeterminado es, randompero puede establecerlo en un valor entero positivo fijo, con0 deshabilitando la función por completo.

Las versiones 2.7 y 3.2 de Python tienen la función deshabilitada de forma predeterminada (use el -Rinterruptor o configurePYTHONHASHSEED=random para habilitarlo); está habilitado de forma predeterminada en Python 3.3 y versiones posteriores.

Si confiaba en el orden de las claves en un conjunto de Python, entonces no lo haga. Python usa una tabla hash para implementar estos tipos y su orden depende del historial de inserción y eliminación , así como de la semilla hash aleatoria. Tenga en cuenta que en Python 3.5 y versiones anteriores, esto también se aplica a los diccionarios.

Consulte también la object.__hash__()documentación del método especial :

Nota : De forma predeterminada, los __hash__()valores de str, bytes y objetos de fecha y hora son "salados" con un valor aleatorio impredecible. Aunque permanecen constantes dentro de un proceso de Python individual, no son predecibles entre invocaciones repetidas de Python.

Esto está destinado a proporcionar protección contra una denegación de servicio causada por entradas cuidadosamente seleccionadas que explotan el peor rendimiento de una inserción de dict, complejidad O (n ^ 2). Ver http://www.ocert.org/advisories/ocert-2011-003.html para obtener más detalles.

El cambio de los valores hash afecta el orden de iteración de los dictados, conjuntos y otras asignaciones. Python nunca ha ofrecido garantías sobre este pedido (y normalmente varía entre compilaciones de 32 y 64 bits).

Vea también PYTHONHASHSEED.

Si necesita una implementación de hash estable, probablemente desee ver el hashlibmódulo ; esto implementa funciones hash criptográficas. El proyecto pybloom utiliza este enfoque .

Dado que el desplazamiento consta de un prefijo y un sufijo (valor inicial y valor final XORed, respectivamente), no puede simplemente almacenar el desplazamiento, por desgracia. En el lado positivo, esto significa que los atacantes tampoco pueden determinar fácilmente la compensación con los ataques de tiempo.

Martijn Pieters
fuente
9
Espero que esto aparezca en los documentos hash () y no solo en __hash __ (). +1 para una gran respuesta. ps ¿No es hashlib una exageración para los usos no criptográficos de las funciones hash?
redlus
1
pybloom usa las funciones hashlib. Pero si quieres algo más rápido, puedes probar pyhash .
Håken Lid
3
¿Por qué la documentación lo llama disablecuando lo establece en 0? No veo la diferencia efectiva para establecerlo en cualquier número de semilla estable antiguo, a menos que me falte algo. Lo que quiero decir es que cuando uso PYTHONHASHSEED=12345, obtengo el mismo hash para cadenas iguales incluso en todas las sesiones, lo mismo sucede cuando lo uso PYTHONHASHSEED=0, el hash para cadenas iguales será el mismo en todas las sesiones (aunque diferente a 12345, pero eso es obvio, así es como las semillas trabajo).
blubberdiblub
@blubberdiblub: sin 0ninguna semilla y los hash de los objetos son iguales a los generados en una versión anterior de Python sin ningún soporte de hashseed.
Martijn Pieters
1
@MartijnPieters ¿Qué significa que los hashes afectados no tengan "ninguna semilla"? ¿Cuál es la diferencia semántica o cualitativa de tener una semilla de, digamos, 12345, aparte del hecho de que crea dos conjuntos distintos de sesiones entre los cuales los valores hash son diferentes y aparte de que PYTHONHASHSEED = 0 es igual a versiones anteriores? ¿Puedes vincularme a un código fuente en particular? Supongo que mi punto es que si no hay tal diferencia, lo llamaría una semilla de 0 y las versiones anteriores de Python solo admiten una semilla de 0. La documentación tal como está ahora es bastante confusa para mí.
blubberdiblub
10

La aleatorización de hash está activada de forma predeterminada en Python 3 . Esta es una característica de seguridad:

La aleatorización de hash está destinada a brindar protección contra una denegación de servicio causada por entradas cuidadosamente seleccionadas que aprovechan el peor rendimiento de una construcción de dictado.

En versiones anteriores de 2.6.8, podía activarlo en la línea de comandos con -R, o la opción de entorno PYTHONHASHSEED .

Puede apagarlo poniéndolo PYTHONHASHSEEDa cero.

Peter Wood
fuente
-9

hash () es una función incorporada de Python y se usa para calcular un valor hash para el objeto , no para la cadena o num.

Puede ver el detalle en esta página: https://docs.python.org/3.3/library/functions.html#hash .

y los valores hash () provienen del método __hash__ del objeto. El doctor dice lo siguiente:

De forma predeterminada, los valores hash () de str, bytes y objetos de fecha y hora están "salados" con un valor aleatorio impredecible. Aunque permanecen constantes dentro de un proceso de Python individual, no son predecibles entre invocaciones repetidas de Python.

Es por eso que tiene un valor hash diferente para la misma cadena en una consola diferente.

Lo que implementa no es una buena manera.

Cuando desee calcular un valor hash de cadena, simplemente use hashlib

hash () tiene como objetivo obtener un valor hash de objeto, no una agitación.

Adam Wen
fuente
6
hash()es perfectamente válido para cadenas o valores numéricos. Está confundiendo esto con el __hash__método personalizado, utilizado porhash() para proporcionar una implementación personalizada del valor hash.
Martijn Pieters