ale*_*xis 29 python hash python-3.x
与Python 3.3开始,哈希算法是不确定性腌避免某种攻击.这对于Web服务器来说很好,但是在尝试调试程序时会很痛苦:每次运行脚本时,dict内容都会以不同的顺序迭代.
一些早期版本的python有一个-R用于启用散列随机化的标志,但现在它是默认行为,该标志尚未被其相反的替换.可以通过设置环境变量来禁用随机化PYTHONHASHSEED:
PYTHONHASHSEED
如果未将此变量设置或设置为random,则使用随机值为str,bytes和datetime对象的哈希值设定种子.
如果将PYTHONHASHSEED设置为整数值,则将其用作固定种子,以生成散列随机化所涵盖类型的散列().
问题是必须在启动python进程之前设置此变量.我试着与设置os.putenv(),或在os.environ,但这些似乎都对散列方法没有效果.这并不太令人惊讶:我不希望python在每一组或字典查找之前检查环境!所以,问题仍然存在:
有没有办法让python程序禁用自己的哈希随机化?
dim*_*414 16
不幸的是,我怀疑这是不可能的.综观test_hash.py的HashRandomizationTests类,并在加入其后代犯介绍了这种行为.他们通过修改环境并使用PYTHONHASHSEED显式设置启动新进程来测试散列行为.您可以尝试复制该模式.
我也注意到你说" 每次我运行我的脚本时,dict内容都会以不同的顺序迭代. " - 我假设你知道collections.OrderedDict,对吧?这是获得可靠的哈希迭代的常规方法.
如果您愿意在shell环境中设置值,您也可以将您的python调用包装在bash脚本中,例如
#! /bin/bash
export PYTHONHASHSEED=0
# call your python program here
Run Code Online (Sandbox Code Playgroud)
这样就可以避免需要操作整个环境,只要您使用包装器脚本即可.
或者甚至只是在命令行上传递值:
$ PYTHONHASHSEED=0 python YOURSCRIPT.py
Run Code Online (Sandbox Code Playgroud)
除了字典顺序,散列随机化也可能破坏hash()直接使用的现有代码。在这种情况下为我解决问题的解决方法是更换
hash(mystring)
和
int(hashlib.sha512(mystring).hexdigest(), 16)
对于 Python 3,mystring.encode('utf-8')标准字符串需要类似的转换。(我正在处理字节字符串。)
注意数字的范围和是否包含负数是不同的。后一种代码提供了更大范围的数字,并且哈希冲突极不可能。
要重现与 相同的 64 位范围hash(),可以将十六进制数字的数量减少到 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)
也许唯一/最干净的方法是将其添加到程序的开头:
import os
import sys
hashseed = os.getenv('PYTHONHASHSEED')
if not hashseed:
os.environ['PYTHONHASHSEED'] = '0'
os.execv(sys.executable, [sys.executable] + sys.argv)
[the rest of your program]
Run Code Online (Sandbox Code Playgroud)
如果PYTHONHASHSEED缺少,它会将其设置为零并用新程序替换当前程序,并提供相同的参数集。根据os.execv:
这些函数都执行一个新的程序,替换当前的进程;他们不回来。在 Unix 上,新的可执行文件被加载到当前进程中,并且与调用者具有相同的进程 ID。错误将报告为 OSError 异常。
当前进程立即被替换。打开的文件对象和描述符不会被刷新,所以如果这些打开的文件上可能有缓冲的数据,你应该在调用 exec* 函数之前使用 sys.stdout.flush() 或 os.fsync() 刷新它们。