Python 3.3 中的哈希函数在会话之间返回不同的结果
- 2025-01-07 08:44:00
- admin 原创
- 101
问题描述:
我在 python 3.3 中实现了 BloomFilter,每次会话都会得到不同的结果。深入研究这种奇怪的行为让我找到了内部 hash() 函数 - 它每次会话都会为同一字符串返回不同的哈希值。
例子:
>>> hash("235")
-310569535015251310
----- 打开一个新的 python 控制台 -----
>>> hash("235")
-1900164331622581997
为什么会发生这种情况?这有什么用?
解决方案 1:
Python 使用随机哈希种子来防止攻击者通过向您发送旨在发生冲突的密钥来对您的应用程序进行 tar-pitting。请参阅原始漏洞披露。通过使用随机种子(在启动时设置一次)抵消哈希,攻击者无法再预测哪些密钥会发生冲突。
PYTHONHASHSEED
您可以通过设置环境变量来设置固定种子或禁用该功能;默认值是,random
但您可以将其设置为固定正整数值,并0
完全禁用该功能。
Python 2.7 和 3.2 版本默认禁用该功能(使用开关-R
或设置PYTHONHASHSEED=random
来启用它);它在 Python 3.3 及更高版本中默认启用。
如果您依赖 Python 集合中键的顺序,则不要这样做。Python 使用哈希表来实现这些类型,并且它们的顺序取决于插入和删除历史记录以及随机哈希种子。请注意,在 Python 3.5 及更早版本中,这也适用于字典。
另请参阅object.__hash__()
特殊方法文档:
注意:默认情况下,
__hash__()
str、bytes 和 datetime 对象的值会使用不可预测的随机值进行“加盐”。虽然它们在单个 Python 进程中保持不变,但在重复调用 Python 时,它们不可预测。这是为了防止因精心选择的输入而导致的拒绝服务,这些输入会利用字典插入的最坏情况性能,即 O(n^2) 复杂度。有关详细信息,请参阅http://www.ocert.org/advisories/ocert-2011-003.html 。
更改哈希值会影响字典、集合和其他映射的迭代顺序。Python 从未对此顺序做出保证(并且通常在 32 位和 64 位版本之间有所不同)。
另请参阅
PYTHONHASHSEED
。
如果您需要稳定的哈希实现,您可能需要查看模块hashlib
;它实现了加密哈希函数。pybloom项目使用此方法。
由于偏移量由前缀和后缀(分别为起始值和最终异或值)组成,因此很遗憾,您不能只存储偏移量。从好的方面来说,这确实意味着攻击者也无法通过计时攻击轻松确定偏移量。
解决方案 2:
hash()
当我尝试比较会话之间数据库中保存的记录时,这种行为让我陷入困境。
解决方案PYTHONHASHSEED
太复杂了,因为我需要我的程序可靠地工作,而不依赖于环境变量设置。
因此我创建了一个简单的 has 函数来哈希字符串(将任何内容转换为字符串都很容易),并生成一个 32 位正整数作为哈希值。这不是一个加密安全的哈希值,但对于快速比较来说已经足够了。
def myHash(text:str):
hash=0
for ch in text:
hash = ( hash*281 ^ ord(ch)*997) & 0xFFFFFFFF
return hash
乘法中的数字只是为了混合位而任意选择的素数。
如果您希望哈希值为十六进制字符串,则可以将最后一行替换为:
return hex(hash)[2:].upper().zfill(8)
解决方案 3:
在 Python 3 中,哈希随机化默认处于启用状态。这是一项安全功能:
哈希随机化旨在提供保护,防止因精心选择的输入而导致的拒绝服务,这些输入会利用字典构造的最坏情况性能
在 2.6.8 之前的版本中,您可以使用 -R 或PYTHONHASHSEED环境选项在命令行中打开它。
PYTHONHASHSEED
您可以通过设置为零来将其关闭。
解决方案 4:
如果您需要从哈希函数获得确定性值,则可以使用以下哈希函数hashlib
:
import hashlib
hash_obj = hashlib.sha256(b"hello")
hex_hash = hash_obj.hexdigest()
print(hex_hash)
# Always prints: 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
模块中有多种可用的哈希函数,更多信息请参阅hashlib 文档。