从python程序中禁用散列随机化

时间:2015-06-01 23:42:03

标签: python python-3.x hash

从Python 3.3开始,散列算法是非确定性salted以避免某种攻击。这对于web服务器来说很不错,但是在尝试调试程序时会很痛苦:每次运行脚本时,dict内容都会以不同的顺序迭代。

某些早期版本的python在启用哈希随机化时有一个-R标志,但现在它是默认行为,该标志尚未被其相反的替换。 可以通过设置环境变量PYTHONHASHSEED来禁用随机化:

  

PYTHONHASHSEED

     

如果未将此变量设置或设置为随机,则使用随机值为str,bytes和datetime对象的哈希值设定种子。
     如果将PYTHONHASHSEED设置为整数值,则将其用作固定种子,以生成散列随机化所涵盖类型的hash()。

问题是必须在启动python进程之前设置此变量。我尝试使用os.putenv()os.environ设置它,但这些似乎对散列方法没有影响。这并不太令人惊讶:我不希望python在每一组或字典查找之前检查环境!所以,问题仍然存在:

python程序有没有办法禁用自己的哈希随机化?

3 个答案:

答案 0 :(得分:15)

不幸的是,我怀疑这是不可能的。查看test_hash.py HashRandomizationTests类及其后代已添加到commit that introduced this behavior中。他们通过修改环境并开始显式设置PYTHONHASHSEED的新进程来测试散列行为。您可以尝试复制该模式。

我也注意到你说" 每次运行我的脚本时,dict内容都会以不同的顺序迭代。" - 我假设您已经了解collections.OrderedDict,对吧?这是获得可靠哈希迭代的正常方法。

如果您愿意在shell环境中设置值,您也可以将您的python调用包装在bash脚本中,例如。

#! /bin/bash
export PYTHONHASHSEED=0

# call your python program here

这样可以避免需要操纵整个环境,只要您使用包装器脚本即可。

甚至只是在命令行上传递值:

$ PYTHONHASHSEED=0 python YOURSCRIPT.py

答案 1 :(得分:1)

也许唯一/最干净的方法是将其放在程序的开头:

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]

如果缺少PYTHONHASHSEED,它将设置为零,并用提供相同参数集的新程序替换当前程序。 根据{{​​1}}:

这些功能全部执行一个新程序,替换当前程序 处理;他们不回来。在Unix上,新的可执行文件已加载 进入当前进程,并具有与 呼叫者。错误将被报告为OSError异常。

当前进程将立即替换。打开文件对象和 描述符不刷新,因此如果这些描述符上可能有数据缓冲 打开文件,您应该使用sys.stdout.flush()刷新它们或 os.fsync(),然后再调用exec *函数。

答案 2 :(得分:-1)

除字典顺序外,哈希随机化还可能破坏直接使用hash()的现有代码。在这种情况下,为我解决问题的一种解决方法是替换

hash(mystring)

使用

int(hashlib.sha512(mystring).hexdigest(), 16)

对于Python 3,标准字符串需要像`mystring.encode('utf-8')这样的转换。 (我正在使用字节字符串。)

请注意,数字范围和是否包含负数是不同的。后面的代码提供了更大的数字范围,并且散列冲突极不可能发生。

要重现与hash()相同的64位范围,可以将十六进制数字的数量减少到16(每位4位),并将结果从最小的负64位数字开始: / p>

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)