The Copenhagen Book: guía para implementar autenticación en aplicaciones web
(thecopenhagenbook.com)- Una guía básica de referencia para diseñar la implementación de autenticación en aplicaciones web, cuyo objetivo es cubrir vacíos en los materiales de autenticación disponibles en línea
- Es un recurso gratuito y de código abierto, mantenido por la comunidad
- Parte del contenido puede estar basado en opiniones o estar incompleto, por lo que conviene usarlo como material complementario en lugar de tomarlo como único criterio
- Se recomienda usarlo junto con OWASP Cheat Sheet Series, un recurso de referencia sobre seguridad de autenticación
- Las sugerencias o inquietudes se pueden comunicar abriendo un nuevo issue, lo que permite participar en la mejora de la documentación
Propósito y naturaleza del documento
- The Copenhagen Book ofrece lineamientos generales de referencia para implementar autenticación (auth) en aplicaciones web
- Su forma de operación es la siguiente
- Se ofrece de forma gratuita
- Es de código abierto
- Es mantenido por la comunidad
- El contenido puede, en ocasiones, reflejar opiniones o no estar completo
Materiales complementarios y formas de participar
- Al implementar autenticación, se recomienda consultarlo junto con OWASP Cheat Sheet Series
- Si tienes sugerencias o inquietudes, puedes abrir un nuevo issue
1 comentarios
Opiniones de Hacker News
Si no me equivoco, este artículo parece estar escrito por el autor de Lucia, una biblioteca popular de autenticación para TypeScript.
Hace poco anunció que considera que la biblioteca Lucia ya no es una forma ergonómica de implementar autenticación, y que va a discontinuarla y reemplazarla por una serie de guías escritas.
La vista previa inicial de las guías se leía bien y también encajaba muy bien con The Copenhagen Book.
https://github.com/lucia-auth/lucia
https://github.com/lucia-auth/lucia/discussions/1707
https://lucia-next.pages.dev/
Vio señales de que la complejidad iba a explotar y, tras admitir que la biblioteca ya ni siquiera le resultaba útil a él, se bajó con humildad del camino típico de la hinchazón de bibliotecas, algo poco común.
En realidad, el 99% de la gente solo necesita iniciar y cerrar sesión, y eso es enormemente útil cuando viene como biblioteca.
Si tienes que ofrecer passkeys de Web 8.0 mediante sockets de curvas elípticas en WASM, puedes hacerlo tú mismo o usar Auth0; pero es raro tirarle la especificación de OAuth y una lista de trampas a alguien que está haciendo una app CRUD de recetas y decirle que implemente la autenticación por su cuenta.
Esa persona debería concentrarse en su idea real en vez de reinventar la plomería, y mucha gente terminará implementándolo mal, quedando en la práctica sin autenticación.
Esta parte es incorrecta: “las direcciones de correo electrónico no distinguen entre mayúsculas y minúsculas”.
https://thecopenhagenbook.com/email-verification
Según el estándar de correo electrónico, las direcciones de correo electrónico sí distinguen entre mayúsculas y minúsculas.
Si durante el envío conviertes un correo a minúsculas, podrías mandarle el mensaje a la persona equivocada, y en autenticación eso es peligroso.
Muchos proveedores de correo conocidos eligen no distinguir mayúsculas de minúsculas, pero un sitio que trata autenticación general debería recomendar el caso general.
https://stackoverflow.com/questions/9807909/are-email-addres...
Además, no siempre está claro cuál es la mayúscula o minúscula opuesta de un carácter, y puede cambiar con el tiempo. Por ejemplo, la ß mayúscula del alemán se agregó a Unicode en 2008, así que en programación general es mejor evitar la distinción de mayúsculas y minúsculas siempre que sea posible.
El estándar dice una cosa, pero las implementaciones se comportan de otra manera, y en este caso seguir solo el texto del estándar causa problemas en el mundo real.
Así, el correo se envía a la dirección con las mayúsculas/minúsculas tal como se ingresó al principio, y el inicio de sesión funciona sin importar las mayúsculas.
La desventaja es que no puedes tener dos usuarios separados con correos que solo difieren en mayúsculas y minúsculas, pero me parece aceptable.
El correo no distinguía mayúsculas y minúsculas, pero el nombre de usuario creado a partir del correo sí las distinguía, así que si creabas una cuenta con un correo que tenía mayúsculas, tenías que escribirlo exactamente con esas mayúsculas para iniciar sesión, y no podías iniciar sesión con el correo ignorando mayúsculas y minúsculas.
Después, si eliminamos también las diferencias de idioma y cultura, desaparecerán decenas de miles de millones de líneas de código; ya puedo escuchar cómo se encogen los repositorios.
Por ejemplo, en un registro de usuario aparece JohnDoe@example.com y en otro johndoe@example.com, cuando claramente se trata de la misma persona.
Y encima era más complicado porque los valores venían de distintos sistemas.
Creo que la matriz de riesgos de los correos que no distinguen mayúsculas y minúsculas es mucho mejor que la de los que sí las distinguen. Es decir, lo correcto es convertir todos los correos a minúsculas, y The Copenhagen Book también acierta en este punto.
Muy bueno. Siempre me molestó que el 90% del material de seguridad parezca hecho para que sea imposible de entender si no eres especialista en seguridad. Eso pasa especialmente con el material de criptografía.
Pero casi todas las páginas aquí me gustan porque son claras, concisas, van al punto y se pueden aplicar de inmediato.
Eso sí, la página sobre curvas elípticas me resultó tan difícil de entender como otros materiales de criptografía.
Creo que su funcionamiento es matemático en sí mismo, así que es inherentemente opaco.
Después de entender los cuerpos finitos, las curvas elípticas y, en la práctica, los grupos de enteros, pude comprender gran parte de la criptografía, y por lo general era de alguna forma una versión del problema del logaritmo discreto.
Sería bueno que hubiera documentos alternativos sobre temas similares. Por ejemplo, algo como el OWASP Cheatsheet, pero desde una perspectiva más práctica.
Lo respeto, pero soy un poco escéptico respecto de este documento.
El nombre es bastante grandilocuente. Llamar al documento como si lo hubieran escrito investigadores universitarios de Copenhagen es un excelente truco de marketing.
Es cierto que Lucia es una biblioteca relativamente popular, pero eso no significa que promueva mejores prácticas ni que debamos ver al autor como una autoridad en esta área tan importante.
También hay partes del diseño de Lucia que no me gustan. Cuando el token del usuario está por expirar, sugiere extender la vida del token existente en lugar de crear un nuevo token de seguridad.
Parece un comportamiento muy inseguro, porque el token en la práctica vive para siempre y puede seguir siendo abusado; además, viola la mejor práctica de seguridad de limitar la vida útil de los tokens.
Tanto Lucia como “Copenhagen Book” recomiendan este enfoque: https://thecopenhagenbook.com/sessions#session-lifetime
Es un enfoque común y normalmente se llama sesión rolling.
El token en sí debería ser inmutable desde el principio, así que no debería poder modificarse su vida útil; por eso, renovar un token consiste en entregar un token nuevo, no en cambiar el token original.
En el primer caso, un atacante roba el token y lo usa para siempre. En el segundo, si el atacante roba el token y obtiene uno nuevo justo antes de que expire, puede seguir suplantando al usuario.
Si el usuario es expulsado quizá note algo, pero es poco probable; además, para una buena experiencia de usuario de todos modos se necesita un período de gracia. Si no, incluso el usuario legítimo tendrá problemas con solicitudes en paralelo.
Se puede usar un segundo token, es decir, un refresh token, pero eso solo traslada el riesgo a ese token. Ahora hay que preocuparse de que el refresh token sea robado y abusado para siempre.
Los refresh tokens son útiles porque no obligan a consultar la base de datos en cada solicitud. Normalmente, un token de sesión de corta duración se puede verificar sin base de datos, como un JWT firmado.
Pero si se roba no se puede invalidar y sigue siendo válido hasta que expire, así que para reducir el daño su expiración debe ser corta.
En cambio, el refresh token consulta la base de datos. No es el segundo token en sí el que agrega seguridad; lo que agrega seguridad es la consulta a la base de datos, porque se puede invalidar eliminándolo de ahí.
Lucia, al menos según los ejemplos, siempre consulta la base de datos, así que se puede invalidar el token en cualquier momento.
Para reducir el riesgo, se debería permitir que el usuario vea y cierre sus sesiones activas. Si es posible, conviene mostrar también hora, ubicación e información del dispositivo. Ej.: “sesión iniciada ayer a las 12 a. m. desde un iPhone en Copenhagen”.
También se podría avisar al usuario cuando haya un inicio de sesión desde una ubicación o dispositivo nuevo.
En definitiva, no hay forma de implementar sesiones de larga duración de manera completamente segura.
Me parece una buena observación, aunque no puedo decir que sea experto.
Hay dos cosas en OAuth que todo el mundo pasa por alto y que no son muy evidentes.
Me alegra que alguien haya señalado una de ellas. Al usar tokens, las transacciones deben usarse sí o sí como un mecanismo de bloqueo distribuido.
Con los refresh tokens, esa importancia se duplica o se cuadruplica. Si se usa el mismo token más de una vez, ese usuario queda desconectado.
No importa si el sistema corre en una máquina o en N máquinas. Si hay dos o más solicitudes con un refresh token en curso al mismo tiempo —algo muy común—, terminas desconectando al usuario y a menudo con un 500.
Los refresh tokens son de un solo uso.
La otra cosa que desarrolladores y frameworks de autenticación pasan por alto es el parámetro “state”.
Viendo la dirección en la que avanza la autenticación hoy en día, parece que se está acercando no a seguridad por oscuridad, sino a inestabilidad por oscuridad.
Si además se involucra el estado de la aplicación, hay que ajustar la lógica de la aplicación a la autenticación, y la aplicación debe verificar si alguien robó un refresh token.
Las transacciones por sí solas no resuelven el problema. Por ejemplo, si abres dos pestañas rápidamente, terminas golpeando el servidor dos veces con el refresh token original.
Ese documento prefiere la rotación de refresh tokens, pero también trata las dificultades obvias en entornos de clúster: https://datatracker.ietf.org/doc/html/rfc6819#section-5.2.2....
Siempre ocurren varias solicitudes al mismo tiempo. Por ejemplo, es común que el navegador arranque con varias pestañas, o que una app de smartphone se inicie y envíe varias solicitudes de golpe.
Me gustaría que más sitios web dieran la opción de “no expirar la sesión hasta que cierre sesión; entiendo el riesgo”.
Hoy en día el botón “remember me” no tiene ningún efecto.
Mi día se interrumpe constantemente por la expiración de sesiones. GitHub me molesta en particular: como lo uso más o menos una vez por semana, la sesión siempre está expirada y además me obliga a usar autenticación de dos factores, así que cada vez tengo que sacar el teléfono e ingresar números.
La experiencia de usuario es horrible, pero además, aunque no tenga pruebas, creo que hacer que la gente inicie sesión todo el tiempo la fatiga y hace que preste menos atención.
Si inicias sesión en un sitio varias veces por semana, para el inicio de sesión número 60 es más fácil que se cuele un sitio de phishing. En cambio, si una cuenta en la que casi nunca tienes que iniciar sesión de pronto te pide la contraseña, se siente raro y aumenta el estado de alerta.
Al final, las empresas tienen que aprender que cada persona tiene distinta tolerancia al riesgo y distinta postura de seguridad, y ofrecer opciones.
De paso, las expiraciones frecuentes de sesión y la autenticación de dos factores de GitHub me irritaron tanto que me pasé a Gitea y desactivé la expiración. El 90% del motivo para migrar fue eso.
Como solo se puede acceder desde mi red, siento que la seguridad incluso mejoró. Es totalmente posible que una empresa pierda clientes por tener un modelo de seguridad inflexible.
En términos generales, si se usó dentro de los últimos 30 días, la sesión se extiende otros 30 días.
https://thecopenhagenbook.com/sessions#session-lifetime
Lo peor es que la sesión parece durar 1 o 2 minutos más que una semana, así que esta semana me expulsó justo después de empezar el trabajo de la semana pasada.
Hace unas semanas inicié sesión para tomar notas justo antes de una reunión recurrente, y durante varias semanas seguidas me obligó a iniciar sesión a mitad de esa reunión.
Hay muchísimos ajustes que pueden anular esos botones de “remember me”.
En lo personal, no he tenido problemas para mantener la sesión iniciada en sitios web, incluido GitHub.
Hace poco conocí el protocolo SRP y me sorprende que no se use ni se mencione más.
Es un protocolo relativamente simple que permite hacer pruebas de conocimiento cero y, de una vez, generar un token de sesión entre el servidor y el cliente.
https://en.wikipedia.org/wiki/Secure_Remote_Password_protoco...
Me gusta este material. Muchos consejos de seguridad son crípticos y a veces se sienten absurdos, como un abogado que aconseja no hacer nada.
Esta guía me gusta porque es refrescantemente concisa, fácil de seguir, fácil de entender y tiene consejos directos.
Voy a seguir leyendo los comentarios por si hay objeciones o advertencias, pero creo que la voy a revisar comparándola con mi proyecto de autenticación.
Lo único que quisiera es que hubiera una sección sobre JWT. Incluso si la opinión del autor es “no lo uses”, estaría bien que lo dijera.
Una de las críticas que suelo hacer a los equipos de seguridad es que dedican mucho tiempo a decir lo que no se debe hacer, en lugar de ofrecer de forma proactiva herramientas o métodos para que la gente haga eficientemente lo que quiere hacer.
El correo puede considerarse un sistema más seguro, y puede que no esté disponible de inmediato o desde cualquier lugar.
Me pregunto si aquí “auth” significa autenticación (authn) o autorización (authz).
Por lo que se ve, parece referirse a autenticación, pero sería bueno aclararlo.
Una queja breve y algo relacionada: los navegadores integrados están arruinando la web.
Los navegadores integrados hacen imposible usar OAuth social. En algunos casos es literalmente imposible, y en otros, prácticamente imposible.
Si tocas un enlace en Instagram, por defecto se abre en el navegador de Instagram; si ese enlace tiene “Sign in with Google”, no funciona porque Google bloquea “navegadores no seguros” como el de Instagram.
Incluso “Sign in with Facebook” tiene problemas, aunque Meta es dueña tanto de Instagram como de Facebook. El navegador integrado de Facebook tiene problemas parecidos.
Cuando tocas un enlace en algo como Slack, respondes un mensaje y luego vuelves al navegador esperando que esa página siga ahí, pero Slack se la tragó dentro de su propio navegador y no está en ninguna parte, casi siempre resulta inútil.
Por suerte, acabo de comprobar que en Slack hay una forma de desactivar el navegador integrado.