# Lo que estaba mal en password.es, y ya no

> Un sitio que se pasa el día diciendo cómo deberían hacerse las cosas tiene la obligación de contar cuándo no las hacía. Esto es lo que encontramos en nuestra propia casa, con las cifras delante.

Publicado el 2026-08-31 por David Carrero · password.es
Original: https://password.es/blog/lo-que-estaba-mal-en-password-es/

---

Este blog lleva doce artículos explicando cómo deberían hacerse las cosas: que la
longitud importa más que los símbolos, que el gestor no es comodidad sino la
única forma de cumplir la regla, que un porcentaje no dice nada.

Es fácil escribir eso. Lo incómodo es mirar tu propio sitio con el mismo rasero.
**Lo hemos hecho, y había tres cosas mal.** Van aquí enteras, con los números que
las delatan, porque un sitio de seguridad que solo publica sus aciertos vale
bastante menos.

## El comprobador se congelaba mientras escribías

El comprobador analiza tu contraseña con [zxcvbn](https://github.com/dropbox/zxcvbn),
una biblioteca que busca patrones en vez de contar mayúsculas. Funciona muy bien y
tiene un coste: **se ejecuta entera, de golpe, en el mismo hilo que dibuja la
página.** Mientras corre, el navegador no puede hacer nada más.

Ese coste crece rápido con la longitud. Medido con el mismo paquete que sirve el
sitio, y con contraseñas de las que la gente escribe de verdad —palabras, años,
sustituciones tipo `@` por `a`—:

| longitud | una ejecución |
|---|---|
| 16 caracteres | 45 ms |
| 20 caracteres | 121 ms |
| 32 caracteres | 273 ms |
| 64 caracteres | 843 ms |
| 128 caracteres | 3.621 ms |

Hasta aquí, normal. El problema estaba en cuántas veces lo lanzábamos.

El campo tenía enganchados **dos** detectores distintos, `input` y `keyup`. Una
tecla normal dispara los dos. Así que cada vez que pulsabas una letra, el análisis
completo corría **dos veces**: 90 ms en una contraseña de 16 caracteres, medio
segundo en una de 32.

Y no había nada que lo frenara: ni un temporizador, ni un límite de longitud, ni
un hilo aparte. A partir de unos veinte caracteres, el navegador tardaba más en
analizar de lo que tú tardabas en pulsar la siguiente tecla. Las pulsaciones se
acumulaban en una cola que ya no se vaciaba nunca, y **la página dejaba de
responder mientras escribías**. Pegar una contraseña larga la congelaba varios
segundos.

El arreglo son dos cambios pequeños. Quitamos el detector de `keyup`, porque
`input` ya cubre teclear, pegar, borrar y el autocompletado del gestor: la mitad
del trabajo desaparece sin cambiar absolutamente nada de lo que ves. Y añadimos
una espera de 150 milisegundos: mientras escribes no se analiza nada, y se
analiza en cuanto paras. Nadie teclea tan despacio como para notarlo.

El resultado es idéntico. `Barcelona1992$` sigue diciendo lo mismo: que
«barcelona» está en el diccionario, que 1992 parece un año, y que cae en menos de
un segundo.

## La descripción estaba cortada a mitad de frase en quince idiomas

Esta es más tonta y más vergonzosa.

La frase que aparece bajo el título en el buscador —la *meta description*— estaba
**truncada en quince de los dieciocho idiomas** del comprobador. Solo se salvaban
el español, el coreano y el japonés. Alguien la recortó por longitud en algún
momento, cortó a mitad de palabra y nadie lo miró después.

En tres idiomas el corte cayó justo en medio de un carácter especial, así que lo
que se publicaba en Google era esto:

| idioma | lo que servíamos | lo que debía decir |
|---|---|---|
| portugués | `…datas e padr&o` | `padrões` |
| turco | `…arıyor ve ger&cced` | `gerçekten` |
| vietnamita | `…chúng tôi tra mật kh&#` | `mật khẩu` |

Lo llamativo es dónde estaba: en **la página que mejor funciona de todo el
sitio**. El comprobador convierte muchísimo mejor que el generador, y lo hacía
con la descripción rota. Están completadas las quince.

De paso, en coreano, japonés y vietnamita hemos movido la marca del principio al
final del título. En el buscador el dominio ya se ve aparte, así que empezar por
«password.es ·» solo gastaba el espacio que más pesa.

## Un sesgo de uno entre cien millones

El generador elige cada carácter con `crypto.getRandomValues()`, que es la fuente
de azar buena del navegador. Pero para convertir ese número enorme en «una
posición del alfabeto» hacíamos una división con resto, y ahí hay una trampa
clásica: 2³² no es múltiplo de 83, así que **las primeras posiciones del alfabeto
salían un pelín más a menudo que las últimas**.

Cuánto es «un pelín»: con un alfabeto de hasta 89 caracteres, del orden de una
parte en cien millones. No cambia nada. No hace tu contraseña más débil de forma
apreciable ni de lejos.

Lo hemos arreglado igualmente, y son tres líneas: se descartan los 45 valores
sobrantes de los 4.294.967.296 posibles y se vuelve a sortear. En un sitio sobre
contraseñas, «es despreciable» no es una buena razón para dejar un sesgo puesto
cuando quitarlo cuesta eso.

## Lo que faltaba: que el navegador hiciera cumplir la promesa

Llevamos un artículo entero explicando que [tu contraseña no sale de aquí](/blog/por-que-no-enviamos-tu-contrasena/).
Era verdad, y aun así era solo una frase nuestra: nada impedía técnicamente que
un script inyectado hiciera otra cosa.

Ahora cada página lleva una política de seguridad de contenido. Las dos partes
que importan:

- **No se ejecuta ningún script que no estuviera en la página al construirla.**
  Cada guion legítimo va firmado con su huella; uno inyectado no coincide y el
  navegador se niega a ejecutarlo.
- **`connect-src 'none'`.** El sitio no hace ni una sola petición de red desde
  JavaScript, y ahora eso lo impone el navegador. Si algún día algo intentara
  mandar a alguna parte lo que escribes, no saldría.

La promesa dejó de ser una declaración y pasó a ser una regla.

## Lo que sigue sin estar bien

**Actualización del 31 de agosto.** Las tres cosas de esta sección se arreglaron el mismo día en que se publicó este artículo: los quince idiomas ya describen el motor real, las dos escalas son una, y el FAQ del comprobador explica lo de los símbolos. Se dejan escritas porque el registro de lo que estuvo mal vale más que la foto de un sitio impecable.

Aquí es donde este artículo se gana el sitio.

**Quince idiomas siguen describiendo el comprobador como el motor antiguo.** El
texto de esas páginas dice que reproduce la lógica clásica de PasswordMeter. Ya
no: ese motor se cambió porque daba «muy fuerte» a `Contraseña1!` y suspendía una
frase larga de cinco palabras, que es exactamente al revés de lo que hay que
decir. El español, el coreano y el japonés están actualizados; los otros quince,
no.

**Hay dos escalas distintas en la misma casa.** La portada decide si una
contraseña es fuerte por sus bits; el comprobador lo decide por el tiempo que
costaría romperla. Son criterios distintos y en ningún sitio lo avisamos.

**El generador y el comprobador no cuentan los mismos símbolos**: uno usa 27, el
otro supone 33. La diferencia es pequeña, pero está ahí.

Ninguna de las tres es un fallo grave. Las tres son deuda, y prefieren estar
escritas aquí que esperando a que las encuentre otro.

## El registro de esta versión

Como el repositorio no es público, el registro de cambios va aquí entero. Versión
**0.2.0**, del 31 de agosto de 2026:

**Añadido.** Política de seguridad de contenido por página, con las huellas de
cada guion. El artículo sobre dónde están tus contraseñas guardadas, en los
dieciocho idiomas. Una cola editorial que decide qué se escribe, y que manda no
publicar nada si se queda vacía.

**Corregido.** La descripción del comprobador, cortada a mitad de frase en quince
idiomas, tres de ellos con un carácter partido por la mitad. El bloqueo de la
pestaña al teclear. El sesgo del sorteo. Y la foto que faltaba en el artículo
anterior.

**Cambiado.** El título del comprobador en coreano, japonés y vietnamita. El
despliegue, que ahora se dispara cuando el sitio construido ya existe y no un
minuto antes. Y las traducciones al vietnamita, hindi y árabe, que dejan de
bloquear una publicación aunque sigan siendo las más flojas.

---

*Las cifras de este artículo están medidas, no estimadas: los tiempos de zxcvbn
con el mismo paquete que sirve el sitio y contraseñas con palabras y
sustituciones; el sesgo del sorteo, calculado sobre los 4.294.967.296 valores de
un entero de 32 bits. El motor del comprobador es
[zxcvbn](https://github.com/dropbox/zxcvbn), de código abierto.*
