在Python程序中禁用哈希随机化

x759pob2  于 2023-08-08  发布在  Python
关注(0)|答案(3)|浏览(136)

从Python 3.3开始,散列算法是不确定的,以避免某种攻击。这对于Web服务器来说很好,但在调试程序时却很麻烦:每次运行脚本时,dict内容都以不同的顺序迭代。
python的一些早期版本有一个-R标志,用于启用散列随机化,但现在它是默认行为,该标志没有被其相反的标志所取代。通过设置环境变量PYTHONHASHSEED可以禁用随机化:

Python籽

如果未设置此变量或将其设置为random,则使用随机值作为str、bytes和datetime对象的哈希的种子。
如果PYTHONHASHSEED设定为整数值,则会当做固定的种子,用来产生杂凑随机化所涵盖之型别的杂凑()。
问题是必须在启动python进程之前设置此变量。我试过用os.putenv()os.environ来设置它,但这些似乎对哈希方法没有影响。这并不太令人惊讶:我不希望python在每次设置或字典查找之前都检查环境!所以,问题依然存在:
有没有办法让python程序禁用自己的散列随机化?

nwnhqdif

nwnhqdif1#

很不幸,我怀疑这是不可能的。查看test_hash.pyHashRandomizationTests类及其后代被添加到引入这种行为的提交中。他们通过修改环境并启动一个显式设置PYTHONHASHSEED的新进程来测试散列行为。也许你可以试着复制这种模式。
我还注意到你说“* 每次我运行脚本时,dict内容都以不同的顺序迭代。*”-我想你知道collections.OrderedDict,对吧?这是获得可靠的哈希迭代的正常方法。
如果你愿意在你的shell环境中设置这个值,你也可以把你的python调用 Package 在一个bash脚本中,例如。

#! /bin/bash
export PYTHONHASHSEED=0

# call your python program here

字符串
这样就避免了需要操纵整个环境,只要您对 Package 器脚本没问题。
或者只是在命令行上传递值:

$ PYTHONHASHSEED=0 python YOURSCRIPT.py

koaltpgm

koaltpgm2#

也许唯一/最干净的方法是在你的程序开始之前添加这个:

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,它会将其设置为零,并将当前程序替换为一个新的,提供相同的参数集。根据os.execv
这些函数都执行一个新程序,取代当前进程;他们不会回来。在Unix上,新的可执行文件被加载到当前进程中,并且将具有与调用者相同的进程ID。错误将报告为OSErerror异常。
立即替换当前进程。打开的文件对象和描述符不会刷新,因此如果这些打开的文件上可能有缓冲的数据,您应该在调用exec* 函数之前使用sys.stdout.flush()或os.fsync()刷新它们。

ep6jt1vc

ep6jt1vc3#

除了字典顺序,散列随机化也可能破坏直接使用hash()的现有代码。在这种情况下,解决了问题的一个变通方法是替换

hash(mystring)

字符串

int(hashlib.sha512(mystring.encode('utf-8')).hexdigest(), 16)

  • hashlib.sha512()
  • hash.hexdigest()

注意,数字的范围和是否包括负数是不同的。后一种代码给出了一个更大的数字范围,并且散列冲突是极不可能的。
要再现与hash()相同的64位范围,可以将十六进制数减少到16(每个数4位),并将结果移位到最小的负64位数开始:

int(hashlib.sha256(mystring.encode('utf-8')).hexdigest()[:16], 16)-(1<<63)


或者,可以使用8个字节并使用int.from_bytes

int.from_bytes(hashlib.sha256(mystring).digest()[:8], byteorder='big', signed=True)

相关问题