Déjenme ingresar números, nada más
(gendignoux.com/blog)- El campo para ingresar el código de verificación por email de 6 dígitos del sistema de inicio de sesión del gobierno suizo AGOV no podía procesar la entrada numérica de teclados franceses AZERTY, lo que bloqueaba por completo el registro de cuentas
- El JavaScript del campo interceptaba las pulsaciones de teclas y guardaba el código de verificación en una variable aparte, mientras ignoraba la tecla Shift; en la práctica, esto bloqueaba la distribución AZERTY, donde se necesita Shift para ingresar números
- El mismo error apareció en varias combinaciones de Firefox, Chromium, Chrome y Safari con Linux y macOS, pero funcionaba bien con teclados QWERTY, lo que acotó la causa a un problema de distribución del teclado, no del navegador ni del sistema operativo
- El soporte oficial también exigía pasar por la misma verificación por email y no ofrecía medios alternativos como correo electrónico o teléfono, así que los usuarios que no podían iniciar sesión ni siquiera podían reportar el bug de inicio de sesión
- Si se usa una lógica compleja de manejo de teclas en vez de un campo de entrada nativo, y además se ata el producto y el canal de soporte al mismo flujo de autenticación, se rompen a la vez la accesibilidad y la respuesta ante fallas; hacen falta una entrada simple basada en DOM y canales de soporte independientes
AGOV como infraestructura de identidad digital
- La identidad digital se ha convertido recientemente en uno de los grandes debates de la web, con múltiples interdependencias y controversias
- Cómo las leyes de verificación de edad afectan el anonimato en línea
- La posibilidad de que Wikipedia deba verificar la identidad de sus usuarios en el Reino Unido
- El problema de depender de iOS y Android oficiales como plataformas obligatorias para la wallet de identidad digital
- El caso de cancelación de cuentas digitales en EE. UU. por ser jueces de la Corte Penal Internacional
- El sistema de inicio de sesión del gobierno suizo AGOV está en operación desde 2024 y ya alcanzó 1.6 millones de cuentas
- Se está expandiendo como método de inicio de sesión para varios trámites gubernamentales, incluido el seguro de desempleo y la declaración obligatoria de impuestos
- En el cantón de Zúrich es el único método de acceso para la solicitud de ciudadanía
Un registro detenido en la etapa de verificación por email
- El proceso de registro de AGOV comienza enviando una dirección de correo y luego ingresando un código de verificación de 6 dígitos
- La UI del código de verificación está compuesta por 6 casillas de entrada, una por cada dígito
- Aunque se ingresen números, el foco no avanza a la siguiente casilla
- Los números se siguen acumulando en la casilla actual, que cambia a estado de error en rojo
- Incluso moviéndose manualmente una casilla a la vez, al final aparece el error
field required
- Se intentó modificar directamente los valores del DOM con herramientas de desarrollador, pero no había valores de formulario visibles y tampoco se registraban errores en la consola web
- El resultado fue el mismo usando otras direcciones de correo o incluso direcciones falsas como
test@example.comexample.comes un dominio reservado según RFC 6761
Se descartaron navegador y sistema operativo como causa
- Al principio se sospechó que la combinación de Firefox y Linux no estaba soportada o que estaba fallando el CAPTCHA
- Justo antes de mostrar el formulario del código de verificación, se conecta a
eu-api.friendlycaptcha.eu - La documentación oficial de requisitos solo menciona Windows y macOS como sistemas operativos compatibles
- En cambio, la guía sobre llaves de seguridad sí indica compatibilidad con Linux y Firefox
- Justo antes de mostrar el formulario del código de verificación, se conecta a
- El mismo error apareció en estas 6 combinaciones de entorno
- Firefox, Chromium y Chrome en Linux
- Firefox, Safari y Chrome en macOS
- En una laptop de trabajo, después de ingresar el código el foco avanzaba automáticamente a la siguiente casilla, y también aparecía correctamente el error
Code entered is incorrectcuando el código era inválido en vez de vacío - En la computadora macOS de un amigo, los tres navegadores también aceptaban el código de verificación
- No era razonable depender de una computadora entregada por el empleador para interactuar a largo plazo con servicios del gobierno, y aun si el registro funcionaba en la computadora del trabajo, seguía existiendo la posibilidad de no poder iniciar sesión después desde una laptop personal
Una estructura de soporte que impedía incluso reportar el problema
- El soporte oficial solo se ofrecía mediante un formulario web, y ese formulario también exigía ingresar primero un código de verificación por email
- El FAQ indica que el soporte de las instituciones participantes solo está disponible en horario laboral mediante la creación de tickets en línea
- No había dirección de correo electrónico ni número telefónico, aparte de la dirección postal de la Cancillería Federal Suiza
- La función de feedback para dejar elogios, críticas o solicitudes sobre AGOV también requería iniciar sesión con AGOV o con una cuenta equivalente
- Los usuarios que fallaban en la verificación por email quedaban atrapados en una dependencia circular: no podían reportar el problema por culpa del mismo flujo de verificación
-
AGOV Access y dispositivos compatibles
- La app AGOV Access para Android tenía una calificación de 1.4, pero en las reseñas de Google Play no se encontró el mismo problema con el código de verificación
- En las reseñas había muchas solicitudes de compatibilidad con GrapheneOS, un sistema derivado de Android centrado en seguridad y privacidad
- Según el FAQ oficial, la app no funciona en GrapheneOS porque su framework de refuerzo contra modificaciones no autorizadas no es compatible
- La Cancillería Federal indicó al proveedor que garantizara la compatibilidad
- Actualmente, los medios de segundo factor permitidos para registro e inicio de sesión son un smartphone estándar aprobado con iOS o Android, o una llave de seguridad; en cualquiera de los casos, primero hay que superar la verificación por email
La causa encontrada en el JavaScript
- La página cargaba pocos recursos, pero el archivo principal de JavaScript, minimizado pero no ofuscado, pesaba 2.4 MB y superaba las 100 mil líneas al expandirse
- Al buscar la cadena
field required, se encontró la lógica que establece el estado del código de verificación- Si no existe la variable del código de verificación, el estado se define como
REQUIRED - Si la longitud no coincide con la requerida, se marca como
WRONG - Si la longitud coincide, se usa
VALIDo un estado de error según el resultado de la verificación del email en el servidor
- Si no existe la variable del código de verificación, el estado se define como
- La función
verificationCodeEnteredintercepta las pulsaciones de teclas y reúne el código de verificación en una variable de JavaScriptEnterejecuta el callback de verificaciónBackspaceborra el valor actual- Las flechas y
Tabmueven el foco entre casillas - Las teclas normales se convierten a número con
Number(S.key)y luego se guardan en el código - Teclas como
Control,Meta,Shift,vyrsimplemente se ignoran y la función retorna
- Como al enviar el formulario no se leen valores del DOM, modificar las casillas con herramientas de desarrollador o extensiones del navegador no afectaba el estado que JavaScript mantenía por separado
Por qué los números solo quedaban bloqueados en AZERTY
- La distribución estándar francesa AZERTY requiere la tecla Shift para ingresar números
- Como el código ignoraba la pulsación de Shift, en AZERTY no era posible guardar los números del código de verificación
- La computadora de trabajo usaba distribución inglesa QWERTY, y entre las personas que ayudaron a verificar el problema no había nadie francés
- Por eso, entre todos los dispositivos probados, solo parecía que estaban rotas dos laptops personales
- Al cambiar la distribución del teclado en el sistema operativo por otra diferente, la entrada volvió a funcionar también en las laptops personales
- A la inversa, al cambiar una máquina que funcionaba bien a distribución francesa, se reprodujo el mismo error
- El teclado estándar en francés de Suiza pertenece a la familia QWERTZ, común en Europa central
- Permite escribir francés y alemán de forma eficiente y no requiere Shift para los números, así que el mismo error no aparece
- Según la Oficina Federal de Estadística de Suiza, hay alrededor de 171 mil residentes de nacionalidad francesa en Suiza, sin contar a los trabajadores transfronterizos que también deben interactuar con el gobierno suizo
Limitaciones para investigar antes de la publicación del código fuente
- Según el FAQ de AGOV, el código fuente de AGOV se publicará en diciembre de 2026 para cumplir con requisitos legales
- El artículo 9 de EMBAG exige que las instituciones federales publiquen el código fuente del software desarrollado directamente o encargado para el cumplimiento de sus funciones
- Se aplican excepciones cuando derechos de terceros o motivos de seguridad impiden o limitan esa publicación
- Lo que se publicará será la versión de AGOV que haya cumplido los objetivos del proyecto, incluido AGOV e-ID Verifier; las versiones posteriores se divulgarán mediante notas de lanzamiento preparadas tras cada release
- Aunque algunos servicios ya usan AGOV como inicio de sesión obligatorio, el código aún no es público, lo que dificultaba que los usuarios avisaran directamente a los desarrolladores o compartieran métodos de mitigación
El alcance de la verificación de identidad comprobado tras registrarse
- Tras cambiar la distribución del teclado, se verificó la dirección de correo y se completó el registro con una llave de seguridad
- Durante el proceso se ingresaron nombre, número telefónico y fecha de nacimiento, pero esos datos no fueron verificados
- En la etapa básica de registro solo se comprobaron dos cosas
- Que la dirección de correo realmente existiera
- Que el segundo factor fuera una llave de seguridad de un modelo permitido o la app AGOV Access ejecutándose en un dispositivo Android o iOS permitido
- AGOV es robusto contra la toma remota de cuentas mediante phishing, pero en la etapa básica de registro no impide que otra persona cree una cuenta usando el nombre y los datos de identidad de alguien más
- Según verificaciones adicionales, para acceder a servicios clasificados como sensibles sí se requiere verificación de identidad
- Justo después del registro todavía no se había intentado acceder a ese tipo de servicios
- El bug fue reportado mediante el formulario de soporte, pero no hubo respuesta
Lecciones de diseño
-
Diseño frágil de entrada y soporte
- No es robusto construir una UI vistosa con 6 campos de entrada y una lógica compleja de JavaScript
- Es preferible usar un solo campo de entrada nativo del navegador y dar forma visual con CSS
- Si la lógica del formulario se separa por completo del DOM, al enviar no se pueden leer los valores del DOM y tampoco los usuarios avanzados o las extensiones del navegador pueden corregir el valor de entrada
- Si solo existe un canal de soporte, cuando ese canal falla desaparece toda forma de contacto; hace falta un segundo canal como email o teléfono
- Si el producto principal y el canal de soporte comparten la misma lógica de login, se pierden los reportes de bugs de quienes no pueden iniciar sesión
- Es la misma razón por la que muchos servicios de internet ponen su página de estado en un dominio separado
- Exigir verificación por email antes de pedir soporte puede reducir el spam, pero también reduce los reportes reales de fallas
- Si el código fuente se publica solo después de volver obligatorio el sistema en algunos entornos, se limita la resolución de problemas por parte de usuarios y la circulación de bugs o workarounds
- La publicación legal del código fuente no es solo una casilla de cumplimiento: también sirve para investigar fallas reales
-
La web abierta ayudó a la investigación
- Como la lógica de JavaScript relacionada no estaba ofuscada, bastaron unas cuantas búsquedas de texto para encontrar la causa
- No hicieron falta decompiladores avanzados ni LLM costosos
- Si el código hubiera estado ofuscado o compilado a WebAssembly, identificar la causa habría tomado mucho más tiempo
- El código no se distribuyó solo en sistemas operativos cerrados y aprobados, sino en la web abierta, lo que permitió descargarlo e investigarlo, aunque estuviera minimizado
-
Condiciones que hicieron posible identificar la causa
- Comparar varias laptops con diferentes distribuciones de teclado permitió descartar temprano problemas específicos del navegador o de la red
- Aun así, recolectar evidencia inicial tomó tiempo, porque hubo que capturar de forma sistemática las pantallas de cada combinación de navegador para confirmar que no se trataba de un error del usuario
1 comentarios
Opiniones en Lobste.rs
Una de las prácticas que más detesto en Internet es cuando un sitio web intercepta las pulsaciones de teclas y las maneja a su antojo, en lugar de dejarlas pasar tal cual al navegador.
Es mucho más común ver sitios que prohíben copiar y pegar en campos de contraseña, pero esto pertenece a la misma categoría y quizá hasta parece más grave.
Por lo general, solo agrega complejidad por motivos forzados.
La modalidad de poner un campo de entrada por cada carácter es como una epidemia.
Una empresa la adoptó porque se veía bien y ahora todo el mundo está implementando su propia versión.
El usuario puede darse cuenta de inmediato, sin leer con atención, de que no se trata de un campo de contraseña.
Un caso relacionado es Medium once had a bug like this which prevented entering the character 'Ś'.
Pensé en otras formas de implementarlo, pero al final no encuentro nada mejor que un
<input type="text" />común y corriente.Incluso me da dudas decorarlo con CSS para que se vea demasiado distinto de un campo normal. Se podría replicar el valor ingresado en casillas grandes, pero habría que responder todo tipo de preguntas, como qué hacer si el usuario ingresa más caracteres que la longitud permitida, y el beneficio no parece tan grande como para justificar el esfuerzo. Como mucho cambiaría la fuente y el tamaño del campo.
1224en lugar de1234.La forma más rápida de corregirlo sería hacer clic en el segundo
2y presionar3, pero eso no se puede implementar solo con HTML+CSS.<input type="text" pattern="[0-9]+" minlength=6 maxlength=6>para que visualmente parezca tener seis casillas.Una forma más fácil sería que el usuario escriba en un único campo de texto y que JavaScript dibuje seis casillas en otra parte del DOM, actualizándolas con el evento
inputque se dispara con cada tecla.Es difícil asegurar cuál de las dos opciones sería mejor para la accesibilidad. Desde el punto de vista de un lector de pantalla, la segunda opción, con un
<input>normal y un<canvas alt="">decorativo, podría ser incluso mejor, y tampoco volvería extraña la operación con teclado. Pero ocultar visualmente el<input>podría ser una catástrofe de accesibilidad para usuarios que emplean ayudas distintas de un lector de pantalla, así que habría que tener mucho cuidado.También se podría, como hacen muchos servicios, ofrecer en el correo electrónico el código junto con un enlace de activación.
Esto también puede resolver el mismo problema.
Se podría pensar que es una situación excepcional, pero la accesibilidad consiste precisamente en manejar bien esas excepciones.
Tridactyl tiene exactamente el mismo problema: https://github.com/tridactyl/tridactyl/issues/3257
Se reportó por primera vez en 2019, y quizá este incidente sirva para que, aunque sea por vergüenza, terminen corrigiéndolo.
Esto vuelve a demostrar que se requiere mucho más esfuerzo para hacer un sitio web roto o lento que para hacer uno que simplemente funcione.