【问题标题】:Disable hash randomization from within python program从 python 程序中禁用哈希随机化
【发布时间】:2015-08-15 14:20:59
【问题描述】:

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

一些早期版本的 python 有一个-R 标志用于启用散列随机化,但现在它是默认行为,该标志并没有被它的反面取代。 可以通过设置环境变量PYTHONHASHSEED来禁用随机化:

PYTHONHASHSEED

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

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

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

【问题讨论】:

  • 这必须在任何实际的 Python 代码执行之前发生;到那时,已经有太多的字符串被散列并放入诸如类型和模块__dict__s 之类的东西中。
  • 我也是这么想的;但我希望知道更多的人可以发表评论。
  • 这些是相关的提交:f4b7ecf8a5f86b7704fe1be1 - 浏览一下我没有立即看到解决方案,但这是一个起点。
  • 这种普通旧散列的随机化实际上不是一个特性。应该有一个完全可靠、不安全、体面的内置散列器,可用于在进程和会话之间创建相同的标识符。 Python3 的哈希器在所有方面或多或少都失败了。如果我们允许,安全任务蠕变将杀死 Python(Perl 已死,Python 也可以); Python3 不是一种安全语言,正如办公室或厨房不是建筑物的安全部分一样。

标签: python python-3.x hash


【解决方案1】:

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

hash(mystring)

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

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

请注意,数字的范围和是否包含负数是不同的。后一个代码给出了更大范围的数字,并且哈希冲突极不可能发生。

要重现与hash() 相同的 64 位范围,可以将十六进制数字的数量减少到 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)

【讨论】:

    【解决方案2】:

    也许唯一/最干净的方法是将其添加到程序的开头:

    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 呼叫者。错误将被报告为 OSError 异常。

    当前进程立即被替换。打开文件对象和 描述符不会被刷新,所以如果可能有数据缓冲在这些 打开文件,您应该使用 sys.stdout.flush() 或 os.fsync() 在调用 exec* 函数之前。

    【讨论】:

      【解决方案3】:

      不幸的是,我怀疑这是不可能的。查看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
      

      【讨论】:

      • 谢谢,这是一个非常有力的暗示。还有一个重生的好技巧——尽管除了丑陋之外,在某些情况下它是不实用的(例如,如果在由远程“内核”提供服务的 ipython 笔记本中运行)。我想我可以在登录时为我的环境设置它...我不会自己进行 DoS。
      • 对于某些应用,值得注意的是整数是通过hash()传递的。
      • @JoachimWagner 我不相信这会影响这个问题所问的哈希随机化。
      • @JoachimWagner 考虑发布一个详细的答案,但我认为这是不正确的。 random.seed() 不会影响 dict 的哈希行为,这就是问题所要问的。
      • 我删除了我的 random.seed() 评论,因为它自 3.2 起不再使用 hash()。感谢@dimo414 的讨论和建议,以发布详细的答案。
      猜你喜欢
      • 2017-03-17
      • 2023-03-18
      • 2011-10-04
      • 2013-01-06
      • 1970-01-01
      • 2016-09-07
      • 1970-01-01
      • 2013-06-17
      • 2013-10-28
      相关资源
      最近更新 更多