Invalidação de tokens em sistemas distribuídos
Nesta página
Construir sistemas rápidos pode ser difícil, e a computação distribuída é muitas vezes a abordagem escolhida para melhorar o desempenho. Isso traz desafios próprios à segurança, em concreto à autenticação e à autorização. Os JSON Web Tokens do OAuth 2 resolvem isso, mas trazem riscos na revogação.
Autenticação distribuída na SEEK
A SEEK conhece bem o modelo de computação distribuída. Quase tudo a que acedes é servido por dezenas de microsserviços escritos em linguagens diferentes e mantidos por equipas diferentes.
Sidecar - uma tarefa coesa ligada à aplicação principal, tipicamente colocada no seu próprio processo ou contentor, que oferece uma interface homogénea aos serviços, seja qual for a linguagem.
Na SEEK resolvemos isto com um sidecar ao lado das nossas aplicações. O sidecar trata da validação dos tokens, aceita ou rejeita os pedidos de entrada e passa à aplicação o contexto do utilizador.
Para servir as muitas linguagens que existem nas aplicações da SEEK, a interface homogénea escolhida é o HTTP. Existe uma implementação em quase todas as linguagens que usamos, e os cabeçalhos são uma boa estratégia para acrescentar informação aos pedidos.
Outra maneira de olhar para o sidecar de autenticação é como um proxy.
Um exemplo do padrão sidecar com dois conjuntos de tarefas num ambiente de cloud com escalonamento automático. Neste cenário, vês um pedido bem-sucedido a passar pelo sidecar até à tarefa da aplicação e um pedido não autorizado a ser terminado no sidecar. Aqui, o sidecar de autenticação usa um JSON Web Key Set (JWKS) em cache para validar a assinatura dos tokens. Isto permite a validação distribuída de tokens sem ter de chamar o fornecedor de identidade em cada pedido.
Este modelo é rápido e escalável, mas infelizmente traz um risco: um utilizador bloqueado continua a poder fazer pedidos até o JSON Web Token (JWT) expirar. Embora a especificação não recomende um prazo de expiração, é prática comum serem de curta duração, cerca de 15 minutos.
Revogação de tokens
Parte do padrão OAuth 2 é a RFC7009, a especificação de revogação de tokens. Descreve o processo, bastante básico, associado à revogação.
A especificação define um endpoint que um cliente pode chamar para submeter o token que quer revogar. Os pedidos de validação seguintes desse token são rejeitados, porque o token passa a ser considerado inválido.
Esta especificação só funciona com tokens opacos, não com JSON Web Tokens. Este desafio existe em muitas empresas, incluindo a SEEK. A nossa arquitetura de sistema distribuído torna extremamente difícil verificar os tokens de forma eficiente contra uma fonte central. É por isso que assinamos os tokens com uma expiração curta e aceitamos o risco de um utilizador bloqueado poder continuar a fazer pedidos até esse token expirar.
Outro desafio é arranjar um mecanismo para identificar o token. Os JSON Web Tokens trazem toda a informação necessária para identificar o utilizador. São empacotados, assinados e enviados, não guardados. Isto significa que é preciso acrescentar uma claim adicional para identificar o token, e para isso temos o JWT ID.
Invalidação distribuída de tokens
Invalidar tokens num sistema distribuído pode ser complicado, mas podemos usar um filtro de Bloom para reduzir bastante o custo de verificar JWT IDs invalidados contra uma fonte central.
Os tokens costumam ser de curta duração e, em qualquer situação, não esperaríamos ter de bloquear 100 000 000 de tokens, que cabem facilmente em cerca de 500 MB de memória na forma de um filtro de Bloom, com 1 hipótese em 999 925 224 de falso positivo.
Para resolver este cenário, tenho andado a trabalhar no oauth-revokerd. O objetivo deste serviço é manter uma lista de JWT IDs revogados e distribuir em conformidade um filtro de Bloom dessa lista.
O serviço oferece uma base de dados em memória para guardar tokens revogados a curto prazo, até à expiração do próprio token. A ideia geral é pô-lo de pé o mais depressa possível com configuração mínima, oferecer uma API de gestão para revogar tokens e uma API de consulta para verificar revogações nos casos de falso positivo.
Neste conceito, o sidecar é alargado para consumir o filtro de Bloom.
Para terminar
É praticamente tudo por agora. Espero fazer mais com este projeto no futuro e vou publicar novidades sobre o progresso no meu site.