(generado a partir de este hilo ya que esto es realmente una cuestión propia y no específica de NodeJS, etc.)
Estoy implementando un servidor API REST con autenticación, y he implementado con éxito el manejo de tokens JWT para que un usuario pueda iniciar sesión a través de un punto final / login con nombre de usuario / contraseña, tras lo cual se genera un token JWT a partir de un secreto del servidor y se devuelve al cliente. Luego, el token se pasa del cliente al servidor en cada solicitud de API autenticada, tras lo cual se utiliza el secreto del servidor para verificar el token.
Sin embargo, estoy tratando de comprender las mejores prácticas sobre exactamente cómo y en qué medida se debe validar el token para crear un sistema verdaderamente seguro. ¿Exactamente qué debería estar involucrado en "validar" el token? ¿Es suficiente que la firma pueda verificarse usando el secreto del servidor, o también debería verificar el token y / o la carga útil del token con algunos datos almacenados en el servidor?
Un sistema de autenticación basado en token solo será tan seguro como pasar el nombre de usuario / contraseña en cada solicitud, siempre que sea igual o más difícil obtener un token que obtener la contraseña de un usuario. Sin embargo, en los ejemplos que he visto, la única información necesaria para producir un token es el nombre de usuario y el secreto del lado del servidor. ¿No significa esto que asumiendo por un minuto que un usuario malintencionado adquiere conocimiento del secreto del servidor, ahora puede producir tokens en nombre de cualquier usuario, teniendo así acceso no solo a un usuario dado como sería el hecho si una contraseña fuera obtenido, pero de hecho a todas las cuentas de usuario?
Esto me lleva a las preguntas:
1) ¿Debería limitarse la validación del token JWT a verificar la firma del token en sí, confiando únicamente en la integridad del secreto del servidor o acompañada de un mecanismo de validación separado?
En algunos casos, he visto el uso combinado de tokens y sesiones de servidor en las que, al iniciar sesión correctamente a través del punto final / login, se establece una sesión. Las solicitudes de la API validan el token y también comparan los datos decodificados que se encuentran en el token con algunos datos almacenados en la sesión. Sin embargo, usar sesiones significa usar cookies y, en cierto sentido, frustra el propósito de usar un enfoque basado en tokens. También puede causar problemas a ciertos clientes.
Uno podría imaginar que el servidor mantiene todos los tokens actualmente en uso en un Memcache o similar, para asegurarse de que incluso si el secreto del servidor se ve comprometido para que un atacante pueda producir tokens "válidos", solo los tokens exactos que se generaron a través del extremo / login sería aceptado. ¿Es esto razonable o simplemente redundante / excesivo?
2) Si la verificación de la firma JWT es el único medio de validar tokens, lo que significa que la integridad del secreto del servidor es el punto de ruptura, ¿cómo se deben gestionar los secretos del servidor? ¿Leer de una variable de entorno y crear (¿aleatoriamente?) Una vez por pila implementada. Renovado o rotado periódicamente (y si es así, cómo manejar los tokens válidos existentes que se crearon antes de la rotación pero necesitan ser validados después de la rotación, tal vez sea suficiente si el servidor se aferra al secreto actual y anterior en un momento dado) ? ¿Algo más?
Tal vez simplemente estoy siendo demasiado paranoico cuando se trata del riesgo de que el secreto del servidor se vea comprometido, que por supuesto es un problema más general que debe abordarse en todas las situaciones criptográficas ...
RSAPrivateKey privateKey
?Respuestas:
También he estado jugando con tokens para mi aplicación. Si bien no soy un experto de ninguna manera, puedo compartir algunas de mis experiencias y pensamientos al respecto.
El objetivo de los JWT es esencialmente la integridad. Proporciona un mecanismo para que su servidor verifique que el token que se le proporcionó es genuino y lo proporcionó su servidor. La firma generada a través de su secreto es lo que prevé esto. Entonces, sí, si su secreto se filtra de alguna manera, esa persona puede generar tokens que su servidor pensaría que son propios. Un sistema basado en token aún sería más seguro que su sistema de nombre de usuario / contraseña simplemente debido a la verificación de la firma. Y en este caso, si alguien tiene su secreto de todos modos, su sistema tiene otros problemas de seguridad con los que lidiar que alguien que hace tokens falsos (e incluso entonces, simplemente cambiar el secreto asegura que cualquier token hecho con el antiguo secreto ahora sea inválido).
En cuanto a la carga útil, la firma solo le dirá que el token que se le proporcionó era exactamente como estaba cuando lo envió su servidor. Verificar que los contenidos de las cargas útiles sean válidos o apropiados para su aplicación obviamente depende de usted.
Para sus preguntas:
1.) En mi experiencia limitada, definitivamente es mejor verificar sus tokens con un segundo sistema. Simplemente validar la firma solo significa que el token se generó con su secreto. Almacenar cualquier token creado en algún tipo de base de datos (redis, memcache / sql / mongo o algún otro almacenamiento) es una forma fantástica de asegurarse de que solo acepta tokens que su servidor ha creado. En este escenario, incluso si se filtra su secreto, no importará demasiado, ya que los tokens generados no serán válidos de todos modos. Este es el enfoque que estoy tomando con mi sistema: todos los tokens generados se almacenan en una base de datos (redis) y en cada solicitud, verifico que el token está en mi base de datos antes de aceptarlo. De esta manera, los tokens se pueden revocar por cualquier motivo, como tokens que se liberaron de alguna manera, cierre de sesión del usuario, cambios de contraseña, cambios secretos, etc.
2.) Esto es algo en lo que no tengo mucha experiencia y es algo que todavía estoy investigando activamente, ya que no soy un profesional de la seguridad. Si encuentra algún recurso, no dude en publicarlo aquí. Actualmente, solo estoy usando una clave privada que cargo desde el disco, pero obviamente eso está lejos de ser la mejor o más segura solución.
fuente
Aquí hay algunas cosas a considerar al implementar JWT en su aplicación:
Mantenga la vida útil de su JWT relativamente corta y administre su vida útil en el servidor. Si no lo hace, y más adelante necesita solicitar más información en sus JWT, tendrá que admitir 2 versiones o esperar hasta que sus JWT anteriores hayan expirado antes de poder implementar su cambio. Puede administrarlo fácilmente en el servidor si solo mira el
iat
campo en el jwt e ignora elexp
campo.Considere incluir la URL de la solicitud en su JWT. Por ejemplo, si desea que su JWT se use en el punto final
/my/test/path
, incluya un campo como'url':'/my/test/path'
en su JWT, para asegurarse de que solo se use en esta ruta. Si no lo hace, es posible que las personas comiencen a usar sus JWT en otros puntos finales, incluso en aquellos para los que no fueron creados. También podría considerar incluir un md5 (url) en su lugar, ya que tener una URL grande en el JWT terminará haciendo que el JWT sea mucho más grande, y puede llegar a ser bastante grande.La expiración de JWT debe ser configurable por cada caso de uso si los JWT se están implementando en una API. Por ejemplo, si tiene 10 puntos finales para 10 casos de uso diferentes para JWT, asegúrese de que puede hacer que cada punto final acepte JWT que caducan en momentos diferentes. Esto le permite bloquear algunos puntos finales más que otros si, por ejemplo, los datos proporcionados por un punto final son muy confidenciales.
En lugar de simplemente vencer los JWT después de un tiempo determinado, considere implementar JWT que admitan ambos:
Todas las fallas de autenticación de JWT deben generar un encabezado de respuesta de "error" que indique por qué falló la autenticación de JWT. por ejemplo, "caducado", "no quedan usos", "revocado", etc. Esto ayuda a los implementadores a saber por qué falla su JWT.
Considere ignorar el "encabezado" de sus JWT, ya que filtran información y dan cierto control a los piratas informáticos. Esto se refiere principalmente al
alg
campo en el encabezado; ignore esto y simplemente asuma que el encabezado es lo que desea admitir, ya que esto evita que los piratas informáticos intenten usar elNone
algoritmo, lo que elimina la verificación de seguridad de la firma.Los JWT deben incluir un identificador que detalle qué aplicación generó el token. Por ejemplo, si sus JWT están siendo creados por 2 clientes diferentes, mychat y myclassifiedsapp, entonces cada uno debe incluir su nombre de proyecto o algo similar en el campo "iss" en el JWT, por ejemplo, "iss": "mychat"
iat
(emitido en) en lugar deexp
(vencimiento) en sus JWT. ¿Por qué? Dado queiat
básicamente significa cuándo se creó el JWT, esto le permite ajustar en el servidor cuándo caduca el JWT, según la fecha de creación. Si alguien fallece enexp
20 años en el futuro, ¡el JWT básicamente vive para siempre! Tenga en cuenta que expirará automáticamente los JWT siiat
es en el futuro, pero deje un poco de margen de maniobra (por ejemplo, 10 segundos), en caso de que la hora del cliente no esté sincronizada con la hora del servidor./mysite/userInfo?jwt=XXX
y que esta URL se almacena en caché. Se desconectan y un par de minutos después, un usuario normal inicia sesión en su aplicación. Obtendrán el contenido en caché, ¡con información sobre un superusuario! Esto tiende a ocurrir menos en el cliente y más en el servidor, especialmente en los casos en los que está utilizando una CDN como Akamai y está dejando que algunos archivos vivan más tiempo. Esto se puede solucionar al incluir la información del usuario relevante en la URL y validarla en el servidor, incluso para solicitudes almacenadas en caché, por ejemplo/mysite/userInfo?id=52&jwt=XXX
fuente
created_by
, ya existe un reclamo para eso en JWT y se llamaiss
(emisor).No creo que sea un experto, pero me gustaría compartir algunas reflexiones sobre Jwt.
1: Como dijo Akshay, es mejor tener un segundo sistema para validar su token.
a .: La forma en que lo manejo: almaceno el hash generado en un almacenamiento de sesión con el tiempo de caducidad. Para validar un token, debe haber sido emitido por el servidor.
b.: Hay al menos una cosa que debe comprobarse en el método de firma utilizado. p.ej :
Algunas bibliotecas que validan JWT aceptarían este sin verificar el hash. Eso significa que sin saber su sal utilizada para firmar el token, un pirata informático podría otorgarse algunos derechos. Asegúrese siempre de que esto no pueda suceder. https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
c .: Usar una cookie con una identificación de sesión no sería útil para validar su token. Si alguien quiere secuestrar la sesión de un usuario de lambda, solo tendrá que usar un rastreador (por ejemplo, wirehark). Este hacker tendría ambas informaciones al mismo tiempo.
La forma en que lo manejo está vinculada al punto 1.a. : Tengo un secreto mezclado con una variable aleatoria. El secreto es único para cada ficha.
Si desea la mejor seguridad posible, no debe seguir ciegamente las mejores prácticas. La mejor manera es comprender lo que está haciendo (creo que está bien cuando veo su pregunta) y luego evaluar la seguridad que necesita. Y si el Mossad quiere tener acceso a sus datos confidenciales, siempre encontrará la manera. (Me gusta esta publicación de blog: https://www.schneier.com/blog/archives/2015/08/mickens_on_secu.html )
fuente
Muchas buenas respuestas aquí. Integraré algunas de las respuestas que creo que son más relevantes y agregaré algunas sugerencias más.
1) ¿Debería limitarse la validación del token JWT a verificar la firma del token en sí, confiando únicamente en la integridad del secreto del servidor o acompañada de un mecanismo de validación separado?
No, por razones no relacionadas con el compromiso de un secreto simbólico. Cada vez que un usuario inicia sesión mediante un nombre de usuario y una contraseña, el servidor de autorización debe almacenar el token que se generó o los metadatos sobre el token que se generó. Piense en estos metadatos como un registro de autorización. Un determinado par de usuarios y aplicaciones solo debe tener un token válido o una autorización en un momento dado. Los metadatos útiles son el ID de usuario asociado con el token de acceso, el ID de la aplicación y la hora en que se emitió el token de acceso (que permite la revocación de los tokens de acceso existentes y la emisión de un nuevo token de acceso). En cada solicitud de API, valide que el token contenga los metadatos adecuados. Debe conservar la información sobre cuándo se emitió cada token de acceso, para que un usuario pueda revocar los tokens de acceso existentes si sus credenciales de cuenta están comprometidas, e iniciar sesión nuevamente y comenzar a usar un nuevo token de acceso. Eso actualizará la base de datos con la hora en que se emitió el token de acceso (la hora de autorización creada). En cada solicitud de API, verifique que la hora de emisión del token de acceso sea posterior a la hora de autorización creada.
Otras medidas de seguridad incluyeron no registrar JWT y requerir un algoritmo de firma seguro como SHA256.
2) Si la verificación de la firma JWT es el único medio de validar tokens, lo que significa que la integridad del secreto del servidor es el punto de ruptura, ¿cómo se deben gestionar los secretos del servidor?
El compromiso de los secretos del servidor permitiría a un atacante emitir tokens de acceso para cualquier usuario, y almacenar los datos del token de acceso en el paso 1 no evitaría necesariamente que el servidor acepte esos tokens de acceso. Por ejemplo, digamos que a un usuario se le ha emitido un token de acceso y, más tarde, un atacante genera un token de acceso para ese usuario. El tiempo de autorización del token de acceso sería válido.
Como dice Akshay Dhalwala, si su secreto del lado del servidor está comprometido, entonces tiene problemas mayores con los que lidiar porque eso significa que un atacante ha comprometido su red interna, su depósito de código fuente o ambos.
Sin embargo, un sistema para mitigar el daño de un secreto de servidor comprometido y evitar el almacenamiento de secretos en el código fuente implica la rotación de token secreto utilizando un servicio de coordinación como https://zookeeper.apache.org. Use un trabajo cron para generar un secreto de la aplicación cada pocas horas más o menos (sin importar el tiempo durante el cual sus tokens de acceso sean válidos) y envíe el secreto actualizado a Zookeeper. En cada servidor de aplicaciones que necesite conocer el secreto del token, configure un cliente ZK que se actualice siempre que cambie el valor del nodo ZK. Almacene un secreto principal y uno secundario y, cada vez que cambie el secreto del token, establezca el secreto del token nuevo en el principal y el secreto del token antiguo en el secundario. De esa manera, los tokens válidos existentes seguirán siendo válidos porque se validarán con el secreto secundario. Para cuando el secreto secundario sea reemplazado por el antiguo secreto primario, todos los tokens de acceso emitidos con el secreto secundario expirarán de todos modos.
fuente
IETF tiene un RFC en progreso en el Grupo de Trabajo de oAuth ver: https://tools.ietf.org/id/draft-ietf-oauth-jwt-bcp-05.html
fuente