Un fallo de inicio de sesión debido a una discrepancia en el almacenamiento de números de teléfono expuso una vulnerabilidad. La cuenta de un usuario fue bloqueada porque la base de datos contenía el mismo número móvil alemán de dos formas distintas: 0171 5550134 en una fila y +49 171 5550134 en otra, por lo que el sistema los trató como entradas diferentes. El resultado: el OTP nunca llegó y el botón de "enviar código" se convirtió en una lotería.
Por qué los números de teléfono son más difíciles que las fechas
Los desarrolladores suelen confiar en una regex para domar la entrada de números de teléfono. Esa confianza se rompe en el momento en que un número cruza una frontera o cambia un plan nacional. Las fechas siguen un calendario predecible; los números de teléfono mutan con los operadores, las regulaciones y las convenciones culturales.
El coste oculto de las cadenas de texto sin procesar
Almacenar un número de teléfono como una cadena de texto simple parece único, hasta que dos representaciones apuntan a la misma línea. Los sistemas OTP que consultan la base de datos en busca de "el" número terminan enviando un código a un duplicado que nunca llega al usuario.
El problema empeora cuando los números se encuentran en campos numéricos. Una columna BIGINT elimina el + inicial y cualquier cero a la izquierda, convirtiendo +49 171 5550134 en 491715550134. Sin el signo más, reconstruir el formato original se convierte en una adivinanza.
El contrato E.164
El plan internacional de numeración telefónica, E.164, define una representación única y portátil:
- Comienza con un
+ - Seguido de un código de país de 1 a 3 dígitos
- Luego el número de abonado
- No más de 15 dígitos en total
- Sin espacios, puntos ni guiones
E.164 no garantiza que el número esté activo; solo garantiza que la cadena siga un patrón estructural válido. Trátelo como un contrato de formato, no como un oráculo de disponibilidad.
Errores comunes que las regex no pueden solucionar
- Almacenamiento numérico – BIGINT descarta el
+y los ceros iniciales. En su lugar, utilice una columna de texto (TEXT o VARCHAR). - Regex codificadas (hard-coded) – Los planes nacionales cambian. México eliminó su prefijo de salida en 2019; Argentina ahora requiere un
9después del código de país para líneas móviles. Un patrón estático queda obsoleto rápidamente. - Eliminación ciega de ceros – Los teléfonos fijos italianos mantienen un cero inicial, los alemanes no. Una regla general de "eliminar ceros iniciales" corrompe los datos italianos mientras deja intactos los números alemanes.
- Asumir que el formato equivale a la entregabilidad – libphonenumber valida la estructura, pero no puede saber si el terminal está encendido o si el número ha sido portado.
Construcción de un flujo de trabajo (pipeline) fiable
- Solicitar el país – Añada un selector de país en los formularios de registro y pase esa región al analizador (parser).
- Mostrar formato en tiempo real – Utilice un formato tipo “AsYouType” para que los usuarios vean el patrón correcto mientras escriben.
- Validar al perder el foco (on blur) – Ejecute la validación después de que el usuario abandone el campo en lugar de en cada pulsación de tecla; esto reduce la fricción.
- Persistir solo la cadena E.164 – Almacene el número normalizado con el prefijo
+en la base de datos. - Formatear en el extremo (at the edge) – Convierta de nuevo a un diseño amigable para el usuario solo en la interfaz de usuario o en las plantillas de correo electrónico.
Al migrar datos heredados (legacy), conserve las filas que fallen la validación. Analice cada entrada existente en una nueva columna, marque los fallos y genere un informe. Las eliminaciones silenciosas crean tickets de soporte que luego se convierten en correcciones costosas.
Una lista de verificación rápida para desarrolladores
- Almacene los números como TEXT/VARCHAR en formato E.164.
- Utilice la biblioteca libphonenumber de Google; gestiona la compleja realidad de los planes globales.
- Proporcione una región predeterminada para los usuarios que omitan el código de país.
- Llame a
is_valid_numberpara registros en vivo; comprueba que el número se ajuste a las reglas regionales. - Utilice
is_possible_numberal limpiar datos masivos; detecta entradas obviamente mal formadas sin rechazar casos limítrofes.
El manejo adecuado de los números de teléfono no es algo opcional; es un requisito previo para cualquier sistema que dependa de un contacto fiable con el usuario. Al normalizar a E.164 y delegar el análisis a una biblioteca probada en combate, los desarrolladores eliminan una clase de errores que erosionan silenciosamente la confianza y los ingresos. Trate los números de teléfono como datos estructurados, no como texto libre, y deje que los estándares hagan el trabajo pesado.
