Python 3.3のハッシュ関数がセッション間で異なる結果を返す


96

私はPython 3.3にBloomFilterを実装し、セッションごとに異なる結果を得ました。この奇妙な動作をドリルダウンすると、内部のhash()関数にたどり着きました。この関数は、セッションごとに同じ文字列に対して異なるハッシュ値を返します。

例:

>>> hash("235")
-310569535015251310

-----新しいpythonコンソールを開く-----

>>> hash("235")
-1900164331622581997

なぜこうなった?なぜこれが便利なのですか?

回答:


136

Pythonはランダムなハッシュシードを使用して、攻撃者が衝突するように設計されたキーを送信することにより、アプリケーションをタールピットするのを防ぎます。元の脆弱性の開示を参照してください。ランダムシード(起動時に1回設定)でハッシュをオフセットすることにより、攻撃者はどのキーが衝突するかを予測できなくなります。

PYTHONHASHSEED環境変数を設定することで、固定シードを設定したり、機能を無効にしたりできます。デフォルトはですrandom0、機能を完全に無効にして、固定の正の整数値に設定できます。

Pythonバージョン2.7および3.2では、この機能がデフォルトで無効になっています(-Rスイッチを使用するか、設定PYTHONHASHSEED=randomして有効にします)。Python 3.3以降ではデフォルトで有効になっています。

Pythonセットのキーの順序に依存している場合は、そうしないでください。Pythonはハッシュテーブルを使用してこれらの型を実装し、その順序は挿入と削除の履歴、およびランダムハッシュシードに依存します。Python 3.5以前では、これは辞書にも当てはまります。

object.__hash__()特別なメソッドのドキュメントも参照してください:

:デフォルトでは__hash__()、str、bytes、およびdatetimeオブジェクトの値は、予測できないランダムな値で「ソルト化」されます。それらは個々のPythonプロセス内で一定のままですが、Pythonの繰り返し呼び出し間で予測することはできません。

これは、dict挿入の最悪の場合のパフォーマンス、O(n ^ 2)の複雑さを悪用する慎重に選択された入力によって引き起こされるサービス拒否に対する保護を提供することを目的としています。詳細については、http://www.ocert.org/advisories/ocert-2011-003.htmlを参照してください。

ハッシュ値を変更すると、辞書、セット、およびその他のマッピングの反復順序に影響します。Pythonはこの順序付けを保証していません(通常、32ビットビルドと64ビットビルドの間で異なります)。

もご覧くださいPYTHONHASHSEED

安定したハッシュ実装が必要な場合は、おそらくhashlibモジュールを確認する必要があります。これは暗号化ハッシュ関数を実装しています。pybloomプロジェクトは、このアプローチを使用しています

オフセットはプレフィックスとサフィックス(それぞれ開始値と最終XOR値)で構成されるため、残念ながらオフセットを格納することはできません。プラス面では、これは攻撃者がタイミング攻撃でもオフセットを簡単に決定できないことを意味します。


8
これは、__ hash __()だけでなく、hash()のドキュメントにも表示されると思います。素晴らしい答えを得るには+1。ps hashlibは、ハッシュ関数を暗号化しないで使用するための過剰ではありませんか?
redlus、2014

1
pybloomはhashlib関数を使用します。しかし、もっと高速なものが必要な場合は、pyhashをチェックしてください
派遣蓋

3
ドキュメントdisableを0に設定するときに、なぜそれを呼び出すのですか?何か不足している場合を除いて、古い安定したシード番号に設定することの効果的な違いはわかりません。PYTHONHASHSEED=12345つまり、私が使用すると、セッション全体で同じ文字列に対して同じハッシュが得られます-使用すると同じことが起こります- PYTHONHASHSEED=0等しい文字列のハッシュは、セッション間で同じになります(12345とは異なりますが、それは明白です)作業)。
blubberdiblub 2017

@blubberdiblub:0シードはまったくなく、オブジェクトのハッシュは、ハッシュシードサポートのない古いバージョンのPythonで生成されたものと同じです。
Martijn Pieters

1
@MartijnPieters影響を受けるハッシュが「シードがまったくない」とはどういう意味ですか?ハッシュ値が異なり、PYTHONHASHSEED = 0が古いバージョンと等しい2つの異なるセッションセットが作成されるという事実を除いて、たとえば12345のシードを持つこととの意味的または質的な違いは何ですか?私を特定のソースコードにリンクできますか?私のポイントは、そのような違いがない場合、私はそれを0のシードと呼び、古いバージョンのPythonは0のシードのみをサポートしているということだと思います。
blubberdiblub

10

Python 3では、ハッシュのランダム化がデフォルトでオンになっています。これはセキュリティ機能です。

ハッシュのランダム化は、dict構造の最悪の場合のパフォーマンスを悪用する慎重に選択された入力によって引き起こされるサービス拒否に対する保護を提供することを目的としています

2.6.8からの以前のバージョンでは、コマンドラインで-RまたはPYTHONHASHSEED環境オプションを使用してオンにすることができました。

PYTHONHASHSEEDゼロに設定することでオフにできます。


-9

hash()はPythonの組み込み関数であり、これを使用してstringやnumではなく、objectのハッシュ値を計算します

詳細については、https//docs.python.org/3.3/library/functions.html#hashのページをご覧ください

hash()の値は、オブジェクトの__hash__メソッドから取得されます。ドキュメントは次のように述べています:

デフォルトでは、str、bytes、およびdatetimeオブジェクトのハッシュ()値は、予測できないランダムな値で「ソルト化」されます。それらは個々のPythonプロセス内で一定のままですが、Pythonの繰り返し呼び出し間で予測することはできません。

そのため、別のコンソールで同じ文字列のハッシュ値が異なります。

あなたが実装することは良い方法ではありません。

文字列のハッシュ値を計算したい場合は、hashlibを使用してください

hash()は、スターリングではなく、オブジェクトのハッシュ値を取得することを目的としています。


6
hash()文字列または数値に対して完全に有効です。これを、ハッシュ値のカスタム実装を提供するため__hash__に使用されるhash()カスタムメソッドと混同しています。
Martijn Pieters
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.