Solo era un avatar
Nueve años enviando el hash de mi contraseña maestra a un servicio de avatares, y cómo lo arreglé.
Llevo más de nueve años usando sss para todas mis contraseñas. Es un generador que no guarda nada: escribes una clave maestra y el nombre del servicio, y te devuelve siempre la misma contraseña. La idea original es de pagoru, y yo tengo mi propia versión en sss.alqubo.es.
Toda la gracia de sss es que la clave maestra no sale de tu navegador. Durante todo ese tiempo, mi versión le había estado mandando algo sacado de ella a un servicio de avatares.
El avatar
La clave maestra no se ve mientras la escribes, así que sss te enseña un dibujo que depende de ella. Si ves el de siempre, la has escrito bien. Es un detalle pequeño, pero muy útil: te ahorra probar contraseñas equivocadas.
El problema estaba en cómo se generaba ese dibujo. No lo hacía la web: se lo pedía a un servicio externo, con el hash de la clave maestra en la URL.
<img src={`https://robohash.org/${hash}?set=set4`} />
hash era el SHA-256 de la clave maestra, tal cual. La versión de 2017 era todavía peor: el servidor recibía la clave en claro en la URL (/clave/servicio) para calcular la contraseña, y el avatar se pedía a adorable.io con el SHA-512 de la clave.
Por qué es un problema
“Es un hash, no la clave”. Y es verdad, pero:
- SHA-256 es rápido a propósito. Está pensado para comprobar que unos datos no han cambiado, no para proteger contraseñas. Con una GPU se prueban miles de millones de hashes por segundo, así que una clave corta o que esté en un diccionario se saca en poco tiempo.
- No tiene sal. La misma clave da siempre el mismo hash, así que sirven las tablas precalculadas.
- Queda en manos de otro. Las URLs acaban en los logs de servidores, proxies y CDNs. No sé qué guardaba robohash, y ese es justo el problema: no debería tener que saberlo.
- Es un identificador. Siempre el mismo hash, desde la misma IP es suficiente para reconocer a alguien entre peticiones.
Nada de esto rompe una buena clave. Pero sss existe precisamente para no tener que fiarte de nadie, y aquí me estaba fiando de un servicio de avatares.
Nueve años sin verlo
Lo más curioso es cuánto tiempo pasó desapercibido. Revisé la generación de contraseñas muchas veces: el hash, la semilla, que salieran todos los tipos de caracteres… Pero el avatar ni lo miraba. Era decoración. Solo era un avatar.
Salió al revisar el código para escribir la página de sss en este blog. Al explicar qué hace cada pieza, esa línea saltaba a la vista. La versión de pagoru, por cierto, nunca tuvo este problema, no usa ningún servicio externo.
Cómo lo arreglé
Cambié el avatar por un identicon, una cuadrícula simétrica de 5×5 que se dibuja en el propio navegador como SVG. Ya no hay ninguna petición, y de paso hice dos cosas más:
const getAvatar = (secret: string): Avatar => {
const bits = parseInt(hasher('sss:avatar:' + secret).slice(0, 6), 16);
const cells = Array.from({ length: 5 }, (_, row) => {
const half = [0, 1, 2].map((col) => ((bits >> (row * 3 + col)) & 1) === 1);
return [...half, half[1], half[0]];
});
const hue = (bits >> 15) % 360;
return { cells, hue };
};
- Un hash solo para el avatar. Antes de calcularlo le añado un prefijo,
sss:avatar:. El resultado no tiene nada que ver con ningún otro hash que salga de la clave, así que no sirve para nada más que para el dibujo. - Solo 24 bits. Del hash uso los 6 primeros caracteres: 15 bits para las celdas (media cuadrícula, la otra mitad es un espejo) y 9 para el color. Hay unos 16 millones de avatares posibles y muchísimas claves dan el mismo, así que, aunque alguien vea el dibujo, apenas le dice nada.
Mi clave maestra es una passphrase larga, así que no he tenido que cambiarla. Si has usado sss.alqubo.es con una clave corta o poco original, te recomiendo cambiarla. Te toca actualizar todas las contraseñas, eso sí.
Lo que me llevo
- Todo lo que sale de un secreto es un secreto. Un hash, un color, un dibujo: si depende de la clave, trátalo igual.
- Lo que parece decoración también es código. Y suele ser el que nadie revisa.
· aplausos