¿Qué es CVE-2026-17084 y por qué importa a tu equipo?
El módulo stringprep de la biblioteca estándar de Python no procesaba correctamente algunos caracteres definidos en las tablas B.2 y B.3 del RFC 3454: en lugar de aplicar las reglas de plegado de mayúsculas (case folding) de Unicode 3.2.0 —la versión que la especificación exige— utilizaba las reglas de la versión de Unicode incluida en el propio intérprete. Esto significa que dos cadenas visualmente idénticas podían codificarse a dominios Punycode distintos, abriendo la puerta a ataques de suplantación, homoglyph y bypass de allowlists.
El aviso fue publicado por Seth Larson, Security Developer-in-Residence en la Python Software Foundation con patrocinio de Alpha-Omega, y quedó registrado como CVE-2026-17084. Según la descripción oficial de NVD, el fallo «solo afecta a nombres de dominio que contienen caracteres que no habían sido registrados o cuyas propiedades Unicode —como el comportamiento de plegado— fueron actualizadas después de Unicode 3.2.0».
¿Cómo un str.lower() se convirtió en una vulnerabilidad?
El código afectado en Lib/stringprep.py lucía así:
👥 ¿Quieres ir más allá de la noticia?
En nuestra comunidad discutimos las tendencias, compartimos oportunidades y nos ayudamos entre emprendedores. Sin humo, solo acción.
👥 Unirme a la comunidaddef map_table_b3(code):
r = b3_exceptions.get(ord(code))
if r is not None: return r
return code.lower()
El problema no está en la lógica sino en la especificación. El RFC 3454 exige Unicode 3.2.0, pero str.lower() usa la versión de Unicode que trae el intérprete. Seth Larson lo demuestra al consultar unicodedata.unidata_version, que devuelve '17.0.0' en builds recientes. Entre 3.2.0 y 17.0.0 el consorcio Unicode modificó las reglas de plegado de decenas de caracteres: Cherokee, tibetano, deseret y otros sistemas de escritura vieron alterado cómo se convierten a minúsculas.
Un ejemplo concreto del propio artículo con la cadena "ᎠᎠ" (dos letras Cherokee, U+13A0):
- Resultado conforme a RFC 3454 →
'xn--58da' - Resultado con
str.lower()de Unicode 17.0.0 →'xn--kz9aa'
Dos dominios Punycode distintos para la misma cadena visible. Esa es exactamente la clase de discrepancia que se explota para registrar dominios casi idénticos a los legítimos y hacer phishing, robo de cookies o evasión de filtros anti-spam.
IDNA 2003 vs IDNA 2008: ¿cuál debería usar tu stack?
El estándar IDNA 2003 es el que está expuesto por la falla: el codec str.encode("idna") y las funciones que dependen de stringprep se rigen por RFC 3491 y RFC 3454. IDNA 2003 fue declarado obsoleto por IDNA 2008 (RFC 5890, 5891, 5892 y 5893), que es el que implementa el paquete externo idna disponible en PyPI.
La recomendación del propio Seth Larson es directa: usa el paquete idna (IDNA 2008), no el codec .encode("idna") (IDNA 2003). La única excepción son los casos donde necesites compatibilidad explícita con sistemas antiguos. En stacks modernos —clientes HTTP, navegadores, librerías de email— IDNA 2008 ya es el comportamiento por defecto, por lo que pocas justificaciones legítimas quedan para mantener 2003.
¿Qué cambios trae el parche y a qué versiones afecta?
La solución, disponible en el repositorio de CPython a través del PR #155293 y sus commits de remediación referenciados por NVD, no reemplaza str.lower(). En su lugar crea una tabla de excepciones para que el comportamiento de str.lower() se parezca al de Unicode 3.2.0 únicamente dentro de las funciones afectadas por stringprep. Así se preserva el resto del comportamiento del intérprete y la especificación vuelve a cumplirse sin tocar el resto del lenguaje.
Volviendo a comparar la cadena de Cherokee con la corrección aplicada:
>>> "ᎠᎠ".encode("idna")→'xn--58da'(alineado con RFC 3454)
Los créditos del aviso: la vulnerabilidad fue reportada por Bitshift, Stan Ulbrych co-desarrolló la remediación y la revisión corrió por cuenta de Marc-Andre Lemburg y Petr Viktorin. El aviso completo también se publicó en la lista oss-security de OpenWall, lo que confirma la divulgación coordinada.
¿Qué significa esto para tu startup?
El caso no es teórico: cualquier servicio que valide o normalice nombres de dominio internacionalizados (IDN) en Python 3 con str.encode("idna") o stringprep directamente podía producir respuestas inconsistentes respecto al estándar. Esto impacta de forma directa a:
- SaaS con autenticación por dominio que hacen allow/denylist de correos o hosts.
- APIs que generan URLs a partir de inputs de usuario y luego las persisten en una base de datos.
- Pipelines anti-phishing o anti-abuso que comparan cadenas IDN contra listas negras.
Acciones concretas que puedes tomar esta semana:
- Audita tu
requirements.txtypyproject.tomlbuscando usos destr.encode("idna")y del módulostringprep. Considera reemplazarlos por el paqueteidnade PyPI (IDNA 2008). - Aplica el parche de Python en cuanto llegue a la versión de tu distribución o imagen base. Si usas imágenes oficiales (
python:3.x-slim), reconstruye en CI/CD para forzar la actualización. - Añade un test de regresión con un carácter afectado —por ejemplo Cherokee U+13A0— que verifique que tu pipeline IDN produce el resultado esperado por RFC 3454, no por el
str.lower()del momento.
Conclusión
CVE-2026-17084 demuestra algo que se repite en seguridad: el bug no es el código, sino la distancia entre el código y la especificación. La función str.lower() de Python es correcta según Unicode 17.0.0, pero IDNA 2003 exige Unicode 3.2.0. La lección para tu equipo: cuando una especificación cita una versión concreta de un estándar externo, el uso de APIs «modernizadas» puede romper la conformidad sin que ningún test lo note. Vale la pena mapear en qué puntos de tu stack conviven lógicas similares —versiones de OpenSSL, códecs de email, parsers de fecha, normalizaciones NFC/NFD— donde una actualización silenciosa del runtime puede invalidar invariantes que dabas por sentadas.
Fuentes
- Seth Larson — When str.lower() is a security vulnerability in Python (fuente original)
- NVD — CVE-2026-17084
- CPython — Pull Request #155293 (remediación)
👥 ¿Quieres ir más allá de la noticia?
En nuestra comunidad discutimos las tendencias, compartimos oportunidades y nos ayudamos entre emprendedores. Sin humo, solo acción.
👥 Unirme a la comunidad













