返回

文章详情

在Python中,str.lower()是一个安全漏洞 – Seth Larson

Hacker News2026年8月25日 20:49

Seth Larson @ 2026-08-18 一些互联网标准仅支持ASCII字符,但世界使用的字符远不止拉丁字母。因此,需要一个将Unicode映射到ASCII的方案以便用于域名。NamePrep是该解决方案的一部分,在RFC 3491中被定义为StringPrep的一个配置文件,并且是应用程序中国际化域名(IDNA)的关键组成部分,也称为“IDNA 2003”。StringPrep算法在RFC 3454中被定义。IDNA 2003已被RFC 5890、5891、5892和5893定义的IDNA 2008所取代。Python通过idna编码器(str.encode('idna'))支持IDNA 2003,而IDNA 2008则由Python包索引上的idna包支持。Python的StringPrep实现是在标准库的stringprep模块中实现的。一般来说,您应该使用idna包(IDNA 2008),而不是.encode('idna')(IDNA 2003),但有时您确实需要旧的行为。StringPrep在第3.2节定义了“大小写折叠”步骤(大小写折叠大致是“如何将代码点转换为小写/大写”),通过映射表B.2和B.3使字符串的比较不区分大小写。B.2实际上是str.lower(),根据Unicode规则将所有字符转为小写,B.3包含例外情况。实现此功能的Python代码(并假设B.3表格捕获正确)如下:def map_table_b3(code):r = b3_exceptions.get(ord(code)) if r is not None: return r return code.lower() 这看起来没问题……标题可能已经透漏了。此函数中的str.lower()调用是一个漏洞!为什么?因为str使用的是特定Python解释器随附的Unicode数据,您可以通过访问unicodedata.unidata_version来确定您的Python解释器使用的Unicode版本: >>> import unicodedata >>> unicodedata.unidata_version '17.0.0' 每个版本的Python上还有一个可用的Unicode 3.2.0数据数据库(unicodedata.ucd_3_2_0),专门用于StringPrep和IDNA算法:$ grep -I 'ucd_3_2_0' -R Lib/ Lib/stringprep.py:from unicodedata import ucd_3_2_0 as unicodedata Lib/encodings/idna.py:from unicodedata import ucd_3_2_0 as unicodedata 这很重要!StringPrep依赖于此特定版本的Unicode以保持一致性,RFC 3454中的表B.2和B.3本质上是编码成表格的Unicode 3.2.0大小写折叠规则。因此,我们需要使用Unicode 3.2.0的大小写折叠规则,而不是更新版本的Unicode规则。这就是为什么调用str.lower()在实现和规范之间存在差异,因此构成漏洞的原因:# RFC 3454 兼容值('Ꭰ'是U+13A0) >>> 'ᎠᎠ'.encode('idna') 'xn--58da' # 如果使用Unicode 17.0.0大小写折叠的值 >>> 'ᎠᎠ'.encode('idna') 'xn--kz9aa' 解决方案是创建新的例外,以便str.lower()在特定功能中表现得像是在使用Unicode 3.2.0。因此,我们遍历每个Unicode代码点,并记录当与Python随附的Unicode版本和Unicode 3.2.0进行比较时的str.lower()行为有所不同。就这样,现在IDNA 2003与规范一致。感谢Bitshift报告此漏洞,感谢Stan Ulbrych共同开发补救办法,感谢Marc-Andre Lemburg和Petr Viktorin审查补救措施。有关更多详细信息,请参见CVE-2026-17084。我作为Python软件基金会的安全开发者驻留员的工作由Alpha-Omega赞助。感谢Alpha-Omega在Python生态系统中支持安全。哇,您已经看到最后了!在Mastodon、电子邮件或Bluesky上与我分享您的想法。浏览这个博客的193条条目存档。查看我在互联网上发现的酷东西列表。通过RSS或电子邮件通讯关注这个博客。去外面走走(最佳选择)

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

请我喝杯咖啡