Passkeys: cómo funciona el reemplazo de la contraseña

Abrís la aplicación del banco, apoyás el dedo, y entrás. No escribiste nada. No hubo un correo con un código, ni una contraseña que recordar, ni un segundo factor que copiar antes de que venza.
Eso es una passkey, y probablemente ya usaste una sin ponerle nombre.
Lo interesante para quien programa no es la comodidad, sino lo que hay detrás: en tu base de datos deja de haber un secreto que robar. Ninguna passkey guardada en tu servidor sirve para entrar a ninguna cuenta, ni siquiera si alguien se lleva la tabla completa.
Vale la pena entender por qué, porque cambia lo que guardás y lo que puede salir mal.
Qué es realmente una passkey
Es un par de claves criptográficas. Una privada, que nunca sale del dispositivo, y una pública, que le das al servidor.
Y de ahí sale la propiedad central: el servidor no guarda ningún secreto. Si alguien roba tu base de datos completa, se lleva claves públicas, que son públicas por definición y no sirven para entrar a ninguna parte.
Comparalo con las contraseñas: aun con un buen algoritmo de derivación, una base robada obliga a que todos cambien la contraseña antes de que alguien descifre las más débiles. Con passkeys no hay nada que descifrar.
Qué ocurre al registrarse
El servidor genera un desafío —bytes al azar— y describe qué quiere:
// En el servidor
const opciones = {
challenge: randomBytes(32), // único, de un solo uso
rp: { name: 'Mi sitio', id: 'misitio.com' },
user: {
id: idBinarioDelUsuario, // NO uses el correo acá
name: 'leo@ejemplo.com',
displayName: 'Leo',
},
pubKeyCredParams: [
{ type: 'public-key', alg: -7 }, // ES256
{ type: 'public-key', alg: -257 }, // RS256
],
authenticatorSelection: {
residentKey: 'required', // la clave se guarda en el dispositivo
userVerification: 'preferred', // huella, rostro o PIN
},
};
Dos detalles que se pasan por alto:
user.id tiene que ser un identificador interno y opaco, no el correo. Va a
quedar guardado en el dispositivo del usuario para siempre. Si usás el correo y
la persona lo cambia, el dispositivo queda con el viejo. Un identificador
aleatorio y estable evita el problema.
residentKey: 'required' es lo que convierte esto en una passkey de verdad.
Hace que la clave quede almacenada en el dispositivo con los datos de la cuenta,
y por eso el usuario puede entrar sin escribir su usuario: el navegador ya sabe
quién es. Sin esa opción, el usuario tiene que identificarse primero.
En el navegador:
const credencial = await navigator.credentials.create({ publicKey: opciones });
Ahí el sistema operativo pide la huella o el rostro, genera el par de claves y devuelve la pública firmada.
El servidor guarda tres cosas:
CREATE TABLE passkeys (
id bytea PRIMARY KEY, -- el identificador de la credencial
usuario_id uuid NOT NULL REFERENCES usuarios(id),
clave_publica bytea NOT NULL,
contador bigint NOT NULL DEFAULT 0,
creada_en timestamptz NOT NULL DEFAULT now(),
ultimo_uso timestamptz,
descripcion text -- "iPhone de Leo"
);
Qué ocurre al entrar
El servidor manda otro desafío. El dispositivo lo firma con la clave privada. El servidor verifica la firma con la clave pública que guardó.
La contraseña nunca viaja porque no existe. Y como la firma incluye el
dominio que la pidió, una página de phishing en misitio.com.ataque.net no
puede obtener una firma válida para misitio.com. El navegador no se la va a
dar. Eso hace a las passkeys resistentes al phishing por construcción, no
por educación del usuario.
El campo contador merece una nota: algunos dispositivos incrementan un número
en cada uso. Si te llega un valor menor o igual al guardado, es señal de que
alguien clonó la credencial. Muchos autenticadores modernos devuelven siempre
cero —los de Apple, por ejemplo—, así que la comprobación es: si el valor que
llega es mayor que cero, tiene que ser mayor que el guardado.
¿Ya es momento?
Dos números alcanzan para responderlo.
El primero es el que convence a quien decide el presupuesto, y no tiene que ver con seguridad: según la FIDO Alliance, la tasa de inicio de sesión exitoso con passkey ronda el 93 %, contra un 63 % de los métodos tradicionales. Cuatro de cada diez intentos con contraseña fallan —olvido, error de tipeo, bloqueo— y cada uno es alguien que abandona la compra o llama a soporte.
El segundo dice que no vas a ser el primero: la misma fuente estima unos 5.000 millones de passkeys en uso, y tres de cada cuatro personas ya tienen al menos una cuenta con passkey. La función existe en todos los navegadores y sistemas operativos actuales.
Con eso dicho, hay tres cosas que los anuncios no cuentan.
Los tres problemas que no se mencionan
1. La recuperación sigue siendo el eslabón débil
Si tu único método de entrada es una passkey en un teléfono y perdés el teléfono, quedás afuera. Los proveedores sincronizan las passkeys en su nube —iCloud, el gestor de Google— lo cual resuelve el caso común, pero te ata a ese ecosistema.
En la práctica esto significa que necesitás un método de recuperación, y ese método suele ser un enlace por correo. Con lo cual la seguridad real de la cuenta pasa a ser la de la casilla de correo. No es un argumento en contra: es un recordatorio de que el eslabón más débil se mudó, no desapareció.
Lo mínimo razonable: exigir al menos dos passkeys registradas antes de permitir desactivar la contraseña.
2. Convivencia larga con las contraseñas
Aunque casi todas las empresas grandes ya están desplegando passkeys, más de la mitad sigue teniendo un método vulnerable al phishing como acceso principal. La transición no es un cambio de interruptor.
Planificá para que ambos convivan un buen tiempo, con la passkey como camino preferido y la contraseña como alternativa. Y medí cuánta gente usa cada uno, porque ese número es el que te va a decir cuándo podés apagar el viejo.
3. El dispositivo prestado y el ajeno
Una passkey vive en un dispositivo. Entrar desde la computadora de otra persona requiere el flujo de dispositivo cruzado: la máquina muestra un código QR, lo escaneás con tu teléfono, y la firma viaja por Bluetooth.
Funciona bien, y es un paso más que la gente no espera. Si tu producto se usa seguido desde equipos compartidos, tenelo en cuenta antes de quitar la alternativa.
Cómo yo lo encararía
No lo implementes desde cero. La especificación WebAuthn tiene muchos
detalles donde equivocarse en silencio: validar el origen, verificar el tipo de
credencial, comprobar la bandera de presencia del usuario. Usá una biblioteca
que verifique todo eso — SimpleWebAuthn en JavaScript es la referencia, y hay
equivalentes en todos los lenguajes.
Si ya usás un proveedor de identidad, probablemente ya lo tenga: .NET 10 sumó passkeys a Identity, y los servicios de autenticación gestionada lo traen como una opción a activar.
El orden que tiene sentido: primero ofrecé la passkey como segundo factor, que no rompe nada. Después como método principal con la contraseña disponible. Y recién cuando los números te digan que casi nadie usa la contraseña, evaluá sacarla.
Comentarios
Iniciá sesión para comentar y dar "me gusta".
Todavía no hay comentarios. Sé la primera persona en escribir uno.