把字符串"ᎠᎠ"(两个切罗基字母)拿去做域名编码,Python给出的答案取决于你用的是哪个版本。旧版本编出xn--58da,装了新版Unicode数据的解释器编出xn--kz9aa。同一段输入,两个完全不同的域名——这不是玩笑,是Python核心开发者Seth Larson上周披露的一个真实漏洞,编号CVE-2026-17084。
问题出在标准库的stringprep模块,它负责给IDNA 2003(老版本的国际化域名标准)做大小写折叠。折叠这一步里混进了一行str.lower(),而这行代码,正是漏洞的起点。
一行str.lower(),漂移出两个域名
IDNA 2003依赖的规范是RFC 3454定义的StringPrep算法,其中规定大小写折叠必须严格对齐Unicode 3.2.0这一个历史版本,不能变。这么规定不是任性,是为了保证不同年代、不同实现的软件,处理同一个域名字符串时得出一致的结果——域名解析、缓存匹配、证书校验,全指望这份一致性。
但str.lower()不认这个规矩。它跟着解释器自带的Unicode数据库走,数据库版本会随Python升级不断往前跑(可以用unicodedata.unidata_version查到当前版本号)。Unicode本身每隔几年就会调整某些字符的大小写规则,str.lower()也跟着变,于是它和StringPrep锁死的3.2.0基准之间,出现了一条会持续变宽的缝。
标准库其实早就留了后手:stringprep.py和encodings/idna.py内部特意导入了unicodedata.ucd_3_2_0这个专用于StringPrep的历史数据表。但案发的这段大小写折叠代码,漏用了普通的str.lower(),绕开了这道保险。
修复方案是CPython PR #155293,已于2026年8月18日合并进main分支,同日向3.15和3.14分支回溯。做法很直接:逐个比对Unicode版本升级前后大小写规则的差异,把差异点全部列成例外表,让str.lower()在这个特定函数里"装作"自己还在用3.2.0。3.16.0之前的所有版本都受影响。
NVD不认"str.lower()是漏洞"这个说法
博客标题说str.lower()是安全漏洞,读起来挺唬人,但NVD给这个CVE的官方描述明确泼了盆冷水:这不是str.lower()方法本身的通用缺陷,问题出在stringprep模块生成的数据表反映了运行时的现代Unicode行为,而不是规范强制要求的3.2.0行为。漏洞类型被归为CWE-436(Interpretation Conflict)——两套系统对同一份输入给出不同解读,这才是根子。
- 结论.漏洞真正的成因是版本锚定失效,而不是某个内置方法"有毒"。把它简化成"str.lower()是安全漏洞",对读者理解风险边界没有帮助,反而容易让人误以为该方法在别的场景也不安全。
这个落差值得记一笔。标题党式的框定和技术定性之间的距离,恰恰说明安全新闻最容易在传播中失真的地方,就是把一个具体的实现失误,包装成一个通用组件的原罪。
更麻烦的是IDNA2003和IDNA2008根本没对齐
这次修复之后,Python的IDNA处理是不是就干净了?没有。Python标准库自带的.encode('idna')走的是2003年的老标准,而PyPI上的idna包(截至这次事件最新版本是3.19)实现的是取代它的IDNA 2008。两套标准对不少边缘字符的处理方式并不一样,切罗基文字的大小写折叠方向就是一个此前已经被报告过的例子——同一个字符串,IDNA2003和IDNA2008能编出方向完全相反的结果。
这才是真正让人不安的地方。域名安全里常见的操作——allowlist校验、SSRF防护里判断目标主机、TLS证书里比对域名——如果一边用标准库的老编码器生成或存储域名,另一边用idna包的新实现去比较,两者对同一个字符串的理解可能根本不是同一个域名。攻击者不需要突破什么加密,只需要找到一个两套标准分歧的字符,就能让校验逻辑"看见"一个域名,实际访问的是另一个。
该怎么办
面向普通读者:这次漏洞影响的是所有低于3.16.0的CPython版本,涉及stringprep和标准库自带的idna编解码器两个模块。日常写代码不太可能主动触碰到str.encode('idna'),但如果你的项目里出现过这行调用,或者依赖某个库间接用了stringprep,值得留意发行版更新节奏。
面向开发者:处理域名安全比较时,别指望标准库的.encode('idna')能兜底,优先用PyPI的idna包并统一走同一套规范化函数——两端都用同一个实现、同一套参数,再做比较,不要一边用老编码器落库、一边用新实现做校验。
- 风险.两套IDNA标准长期并存,任何跨系统做域名比对的代码,都可能在极少数字符上被撬开一条缝。
古人说"名不正则言不顺",域名系统对"名"的较真程度比大多数程序员想象得高。一个大小写折叠的实现细节,足够让同一串字符在两套标准里变成两个地址——这事没有那么戏剧化,却比很多被吹得很响的"AI安全漏洞"更贴近真实的攻击面。
