Python 3.3で始まると、ハッシュアルゴリズムは特定の種類の攻撃を回避するために非決定的です salted 。これはWebサーバーにとってはいいことですが、デバッグしようとすると苦痛ですプログラム:スクリプトを実行するたびに、dictの内容が異なる順序で繰り返されます。
pythonの以前のバージョンの一部には、enableハッシュランダム化用の_-R
_フラグがありましたが、今ではデフォルトの動作では、フラグはその逆に置き換えられていません。ランダム化は環境変数 PYTHONHASHSEED
を設定することで無効にできます:
[〜#〜] pythonhashseed [〜#〜]
この変数が設定されていないかランダムに設定されている場合、ランダムな値を使用してstr、bytes、およびdatetimeオブジェクトのハッシュがシードされます。
PYTHONHASHSEEDが整数値に設定されている場合、これは、ハッシュのランダム化の対象となるタイプのhash()を生成するための固定シードとして使用されます。
問題は、pythonプロセスを起動する前にこの変数を設定する必要があることです。os.putenv()
または_os.environ
_で設定しようとしましたが、これらはハッシュ法には影響がないようです。これはそれほど驚くべきことではありません:pythonがすべての単一のセットまたは辞書のルックアップの前に環境をチェックすることを期待しません!したがって、質問は残ります:
pythonプログラムが独自のハッシュのランダム化を無効にする方法はありますか?
残念ながら、これは不可能だと思います。見つめている - test_hash.py
HashRandomizationTests
クラスとその子孫が この動作を導入したコミット に追加されました。彼らは、環境を変更し、PYTHONHASHSEED
を明示的に設定して新しいプロセスを開始することにより、ハッシュ動作をテストします。おそらく、そのパターンをコピーしてみることができます。
「スクリプトを実行するたびに、dictの内容は異なる順序で繰り返されます。」-あなたが知っていると思います collections.OrderedDict
でしょ?これが、信頼できるハッシュ反復を取得する通常の方法です。
シェル環境で値を設定する場合は、pythonの呼び出しをbashスクリプトでラップすることもできます。
#! /bin/bash
export PYTHONHASHSEED=0
# call your python program here
これにより、ラッパースクリプトに問題がなければ、環境全体を操作する必要がなくなります。
または、コマンドラインで値を渡すだけでもかまいません。
$ PYTHONHASHSEED=0 python YOURSCRIPT.py
辞書の順序とは別に、ハッシュのランダム化はhash()
を直接使用する既存のコードを壊す可能性もあります。この場合の問題を解決した回避策は、
hash(mystring)
と
int(hashlib.sha512(mystring).hexdigest(), 16)
Python 3の場合、標準文字列には `mystring.encode( 'utf-8')のような変換が必要になります(バイト文字列で作業していました)。
数値の範囲と、負の数値が含まれるかどうかは異なることに注意してください。後者のコードは、より大きな範囲の数値を提供し、ハッシュの衝突は非常に起こりそうにありません。
hash()
と同じ64ビット範囲を再現するには、16進数の桁数を16(桁あたり4ビット)に減らし、結果を最小の負の64ビット番号から開始するようにシフトします。
int(hashlib.sha256(mystring).hexdigest()[:16], 16)-2**63
または、8バイトを使用して_int.from_bytes
_を使用することもできます。
int.from_bytes(hashlib.sha256(mystring).digest()[:8], byteorder='big', signed=True)