When str.lower() is a security vulnerability in Python – Seth Larson
rbanffy
92 points
42 comments
August 25, 2026
Related Discussions
Found 5 related stories in 50.6ms across 4,390 title embeddings via pgvector HNSW
- Why older tech is sometimes safer from hackers tchalla · 86 pts · August 24, 2026 · 38% similar
- Lessons Learned from CISA's Recent GitHub Leak Bender · 14 pts · July 13, 2026 · 38% similar
- Build your own vulnerability harness ianrahman · 32 pts · July 10, 2026 · 37% similar
- Tile's security is so bad it's a feature for stalkers sambellll · 154 pts · July 25, 2026 · 37% similar
- Python's pre-declared constants are kinda weird rbanffy · 167 pts · August 25, 2026 · 36% similar
Discussion Highlights (14 comments)
echoangle
> This is why calling str.lower() represents a difference in the implementation and the specification, and therefore a vulnerability: I wish there was some explanation how this is a vulnerability and not just a bug generating erroneous data. Vulnerability for me sounds like there’s a reasonable way to create an exploit from the bug, and I don’t see one here as someone who’s not very familiar with the topic.
cat-whisperer
the horrors of versioning
tialaramex
This idiocy is a big part of why it was so important to get Python people working on TLS implementations to understand that the defined mechanism for SANs (no the "alternative" in Subject Alternative Name doesn't mean in the sense of more than one, X.509 is originally for the X.500 system and the Internet repurposed X.509 so these are alternative names from the Internet) says that these are DNS names, they specifically are not to be understood as some sort of human readable text, and thus "decoding" them to Unicode is definitely nonsense even though Python really wanted to do that and I think used to do it or at least proposed to. The rule for how SAN DnsNames match againt like names, from the DNS is very, very simple so that you don't screw it up. You handle a single wildcard (ASCII * code 42 matches any single DNS label) and beyond that it's literally byte comparison. You don't care what these bytes mean, either the bytes are all identical or that's not a match and we're done.
tescreal
it would be good to look for ".lower()" squatters if you host on an idn.
K0IN
I was also startled when python did ß.upper() returns "SS". Which is kind of unsuspected in some cases (if string length changes with an upper call)
jooon
Reminds me of an old security incident at Spotify https://engineering.atspotify.com/2013/06/creative-usernames
bawolff
That's a little over the top. string.lower() is not a security vulnerability. Not following the spec is the security vulnerability.
ittsel
Is that a real thing though?
ittsel
if you have a software system that contains two different implementations of IDNA 2003 processing user input" Is that a real thing though? Is someone doing that?
ajd555
So the attack surface would be a bit flip on a domain name? Or more specifically, a unicode conversion flip, where an attacker could redirect to a malicious IP? Impressive to have found such a vulnerability!
ummonk
> 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. So, we go through each Unicode codepoint and record when the behavior of str.lower() is different when comparing the Unicode version shipped with Python and Unicode 3.2.0 This sounds like a really hacky solution compared to implementing a separate frozen Unicode 3.2.0 lower.
drdexebtjl
All 6 users of IDNs must be really worried right now.
ike_sh
Hit this with the Kelvin sign once. Took embarrassingly long to track down.
inigyou
If this is so important to know you probably shouldn't serve 403 errors to people.