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

Leonardo Gurgitano6 minLeer en inglés

Objeto único fotografiado en blanco y negro sobre fondo oscuro.
Objeto único fotografiado en blanco y negro sobre fondo oscuro.

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