HeadlinesBriefing favicon HeadlinesBriefing.com

Python str.lower() IDNA Security Bug Fixed

Hacker News •
×

Some internet standards only support ASCII characters, but the world uses much more than the Latin alphabet. Thus, a mapping from Unicode to ASCII for use in domain names is required. Name Prep was part of that solution, defined in RFC 3491 as a profile of String Prep, and is crucially a component of Internationalizing Domain Names in Applications (IDNA), also known as “IDNA 2003”. The String Prep algorithm is defined in RFC 3454. IDNA 2003 has been obsoleted by IDNA 2008 defined in RFC 5890, 5891, 5892, and 5893. Python supports IDNA 2003 through the idna codec (str.encode('idna')) and IDNA 2008 is supported by the idna package on the Python package Index. Python's implementation of String Prep is implemented in the stringprep module in the standard library.

In general, you should be using the idna package (IDNA 2008) and not .encode("idna") (IDNA 2003), but sometimes you do need the older behavior. String Prep defines the “case folding” step (case folding is approximately “how to lowercase/uppercase a codepoint”) in Section 3.2, enabling case-insensitive comparisons of strings, by mapping all characters through mapping tables B.2 and B.3. B.2 is effectively str.lower(), lowercasing all characters according to Unicode rules and B.3 contains the exceptions.

The str.lower() call in this function is a vulnerability! Because str uses whatever Unicode data that the particular Python interpreter is shipped with, you can figure out what Unicode version your Python interpreter uses by accessing unicodedata.unidata_version. There's also a database of Unicode 3.2.0 data available on every version of Python (unicodedata.ucd_3_2_0) specifically for the String Prep and IDNA algorithms. String Prep depends on this specific version of Unicode to operate consistently, the B.2 and B.3 tables in RFC 3454 are essentially Unicode 3.2.0 case-folding rules encoded into a table. So we need to use Unicode 3.2.0 case-folding rules, not newer Unicode case-folding rules.

The fix was to create new exceptions so that str.lower() would behave as if it was using Unicode 3.2.0 for only particular function. Thanks to Bitshift for reporting the vulnerability, Stan Ulbrych for co-developing the remediation, and Marc-Andre Lemburg and Petr Viktorin for reviewing the remediation. See CVE-2026-17084 for more details.