(gerado a partir deste tópico, pois esta é realmente uma questão própria e não específica do NodeJS etc)
Estou implementando um servidor REST API com autenticação e implementei com êxito o manuseio de tokens JWT para que um usuário possa fazer login por meio de um ponto de extremidade / login com nome de usuário / senha, no qual um token JWT é gerado a partir de um segredo de servidor e retornado ao cliente. O token é então passado do cliente para o servidor em cada solicitação de API autenticada, na qual o segredo do servidor é usado para verificar o token.
No entanto, estou tentando entender as melhores práticas para saber exatamente como e em que medida o token deve ser validado, para fazer um sistema verdadeiramente seguro. O que exatamente deve estar envolvido na "validação" do token? É suficiente que a assinatura possa ser verificada usando o segredo do servidor ou devo também verificar o token e / ou carga útil do token em relação a alguns dados armazenados no servidor?
Um sistema de autenticação baseado em token só será tão seguro quanto passar nome de usuário / senha em cada solicitação, desde que seja tão ou mais difícil obter um token do que obter a senha de um usuário. No entanto, nos exemplos que vi, as únicas informações necessárias para produzir um token são o nome de usuário e o segredo do servidor. Isso não significa que, supondo por um minuto que um usuário malicioso obtenha conhecimento do segredo do servidor, ele agora pode produzir tokens em nome de qualquer usuário, tendo assim acesso não apenas a um determinado usuário, como aconteceria se uma senha fosse obtido, mas na verdade para todas as contas de usuário?
Isso me leva às perguntas:
1) A validação do token JWT deve se limitar a verificar a assinatura do próprio token, contando apenas com a integridade do segredo do servidor ou acompanhada por um mecanismo de validação separado?
Em alguns casos, tenho visto o uso combinado de tokens e sessões de servidor em que, após o login bem-sucedido por meio do endpoint / login, uma sessão é estabelecida. As solicitações de API validam o token e também comparam os dados decodificados encontrados no token com alguns dados armazenados na sessão. No entanto, usar sessões significa usar cookies e, de certa forma, isso vai contra o propósito de usar uma abordagem baseada em tokens. Também pode causar problemas para alguns clientes.
Pode-se imaginar o servidor mantendo todos os tokens atualmente em uso em um memcache ou semelhante, para garantir que, mesmo que o segredo do servidor seja comprometido para que um invasor possa produzir tokens "válidos", apenas os tokens exatos que foram gerados por meio do endpoint / login seria aceito. Isso é razoável ou apenas redundante / exagero?
2) Se a verificação da assinatura JWT for o único meio de validar tokens, ou seja, a integridade do segredo do servidor é o ponto de quebra, como os segredos do servidor devem ser gerenciados? Ler a partir de uma variável de ambiente e criar (aleatoriamente?) Uma vez por pilha implantada? Renovado ou girado periodicamente (e se sim, como lidar com tokens válidos existentes que foram criados antes da rotação, mas precisam ser validados após a rotação, talvez seja o suficiente se o servidor mantiver o segredo atual e o anterior a qualquer momento) ? Algo mais?
Talvez eu esteja simplesmente sendo paranóico demais quando se trata do risco de o segredo do servidor ser comprometido, o que é, obviamente, um problema mais geral que precisa ser resolvido em todas as situações criptográficas ...
RSAPrivateKey privateKey
??Respostas:
Também tenho usado tokens para meu aplicativo. Embora eu não seja um especialista de forma alguma, posso compartilhar algumas de minhas experiências e pensamentos sobre o assunto.
O objetivo dos JWTs é essencialmente integridade. Ele fornece um mecanismo para que seu servidor verifique se o token fornecido a ele é genuíno e foi fornecido por seu servidor. A assinatura gerada por meio do seu segredo é o que fornece isso. Então, sim, se o seu segredo vazar de alguma forma, esse indivíduo pode gerar tokens que o seu servidor pensaria serem seus. Um sistema baseado em token ainda seria mais seguro do que seu sistema de nome de usuário / senha simplesmente por causa da verificação de assinatura. E, neste caso, se alguém tiver seu segredo de qualquer maneira, seu sistema terá outros problemas de segurança para lidar além de alguém que está criando tokens falsos (e, mesmo assim, apenas alterar o segredo garante que todos os tokens feitos com o segredo antigo serão inválidos).
Quanto à carga útil, a assinatura só dirá que o token fornecido a você estava exatamente como estava quando o servidor o enviou. verificar se os conteúdos das cargas úteis são válidos ou apropriados para o seu aplicativo depende obviamente de você.
Para suas perguntas:
1.) Na minha experiência limitada, é definitivamente melhor verificar seus tokens com um segundo sistema. A simples validação da assinatura significa apenas que o token foi gerado com o seu segredo. Armazenar quaisquer tokens criados em algum tipo de banco de dados (redis, memcache / sql / mongo ou algum outro armazenamento) é uma maneira fantástica de garantir que você aceite apenas tokens criados por seu servidor. Nesse cenário, mesmo se seu segredo vazar, não terá muita importância, pois os tokens gerados não serão válidos de qualquer maneira. Esta é a abordagem que estou adotando com meu sistema - todos os tokens gerados são armazenados em um banco de dados (redis) e, em cada solicitação, verifico se o token está em meu banco de dados antes de aceitá-lo. Desta forma, os tokens podem ser revogados por qualquer motivo, como tokens que foram liberados de alguma forma, logout do usuário, alterações de senha, alterações de segredo, etc.
2.) Não tenho muita experiência e ainda estou pesquisando ativamente, pois não sou um profissional de segurança. Se você encontrar algum recurso, fique à vontade para publicá-lo aqui! Atualmente, estou usando apenas uma chave privada que carrego do disco, mas obviamente essa está longe de ser a melhor ou a mais segura solução.
fonte
Aqui estão algumas coisas a serem consideradas ao implementar JWTs em seu aplicativo:
Mantenha o tempo de vida do JWT relativamente curto e faça com que ele seja gerenciado no servidor. Se você não fizer isso, e posteriormente precisar exigir mais informações em seus JWTs, você terá que oferecer suporte a 2 versões ou esperar até que seus JWTs mais antigos tenham expirado antes de implementar sua alteração. Você pode gerenciá-lo facilmente no servidor se olhar apenas para o
iat
campo no jwt e ignorar oexp
campo.Considere incluir o url da solicitação em seu JWT. Por exemplo, se você deseja que seu JWT seja usado no ponto de extremidade
/my/test/path
, inclua um campo como'url':'/my/test/path'
em seu JWT, para garantir que ele só seja usado neste caminho. Do contrário, você pode descobrir que as pessoas começam a usar seus JWTs em outros terminais, mesmo aqueles para os quais não foram criados. Você também pode considerar a inclusão de um md5 (url), pois ter um grande url no JWT fará com que o JWT seja muito maior e eles podem ficar bem grandes.A expiração do JWT deve ser configurável para cada caso de uso se os JWTs estiverem sendo implementados em uma API. Por exemplo, se você tiver 10 terminais para 10 casos de uso diferentes para JWTs, certifique-se de fazer com que cada terminal aceite JWTs que expiram em momentos diferentes. Isso permite que você bloqueie alguns terminais mais do que outros, se, por exemplo, os dados servidos por um terminal são muito confidenciais.
Em vez de simplesmente expirar JWTs após um certo tempo, considere implementar JWTs que suportem ambos:
Todas as falhas de autenticação JWT devem gerar um cabeçalho de resposta de "erro" que indica por que a autenticação JWT falhou. por exemplo, "expirado", "sem usos restantes", "revogado" etc. Isso ajuda os implementadores a saber por que o JWT está falhando.
Considere ignorar o "cabeçalho" de seus JWTs conforme eles vazam informações e fornecem uma medida de controle para os hackers. Isso se refere principalmente ao
alg
campo no cabeçalho - ignore isso e apenas suponha que o cabeçalho é o que você deseja oferecer suporte, pois isso evita que hackers tentem usar oNone
algoritmo, o que remove a verificação de segurança da assinatura.Os JWTs devem incluir um identificador detalhando qual aplicativo gerou o token. Por exemplo, se seus JWTs estão sendo criados por 2 clientes diferentes, mychat e myclassifiedsapp, cada um deve incluir o nome do projeto ou algo semelhante no campo "iss" no JWT, por exemplo, "iss": "mychat"
iat
(emitido em) em vez deexp
(expiração) em seus JWTs. Por quê? Comoiat
basicamente significa quando o JWT foi criado, isso permite que você ajuste no servidor quando o JWT expirar, com base na data de criação. Se alguém morrer emexp
20 anos no futuro, o JWT basicamente viverá para sempre! Observe que você expira automaticamente os JWTs se elesiat
estiverem no futuro, mas permita um pouco de espaço de manobra (por exemplo, 10 segundos), caso o horário do cliente esteja ligeiramente fora de sincronia com o horário do servidor./mysite/userInfo?jwt=XXX
e que esse url seja armazenado em cache. Eles se desconectam e, alguns minutos depois, um usuário regular faz login em seu aplicativo. Eles obterão o conteúdo em cache - com informações sobre um superusuário! Isso tende a acontecer menos no cliente e mais no servidor, especialmente nos casos em que você está usando um CDN como o Akamai e está permitindo que alguns arquivos durem mais. Isso pode ser corrigido incluindo as informações relevantes do usuário na url e validando-as no servidor, mesmo para solicitações em cache, por exemplo/mysite/userInfo?id=52&jwt=XXX
fonte
created_by
chama de, já existe uma reclamação para isso no JWT e é chamadoiss
(emissor).Não acho que seja um especialista, mas gostaria de compartilhar algumas idéias sobre Jwt.
1: Como Akshay disse, é melhor ter um segundo sistema para validar seu token.
a .: A maneira como eu lido com isso: eu armazeno o hash gerado em um armazenamento de sessão com o tempo de expiração. Para validar um token, ele precisa ter sido emitido pelo servidor.
b.:Há pelo menos uma coisa que deve ser verificada no método de assinatura usado. por exemplo :
Algumas bibliotecas que validam o JWT aceitariam este sem verificar o hash. Isso significa que, sem saber o sal usado para assinar o token, um hacker pode conceder a si mesmo alguns direitos. Sempre certifique-se de que isso não aconteça. https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
c .: Usar um cookie com um Id de sessão não seria útil para validar seu token. Se alguém quiser sequestrar a sessão de um usuário lambda, basta usar um sniffer (por exemplo: wirehark). Este hacker teria as duas informações ao mesmo tempo.
A forma como trato disso está ligada ao ponto 1.a. : Eu tenho um segredo misturado com uma variável aleatória. O segredo é único para cada token.
Se você deseja a melhor segurança possível, não deve seguir cegamente as práticas recomendadas. A melhor maneira é entender o que você está fazendo (acho que está tudo bem quando vejo sua pergunta) e, em seguida, avaliar a segurança de que você precisa. E se o Mossad quiser ter acesso aos seus dados confidenciais, eles sempre encontrarão uma maneira. (Gosto desta postagem do blog: https://www.schneier.com/blog/archives/2015/08/mickens_on_secu.html )
fonte
Muitas respostas boas aqui. Vou integrar algumas das respostas que considero mais relevantes e adicionar mais algumas sugestões.
1) A validação do token JWT deve se limitar a verificar a assinatura do próprio token, contando apenas com a integridade do segredo do servidor ou acompanhada por um mecanismo de validação separado?
Não, por motivos não relacionados ao comprometimento de um segredo de token. Cada vez que um usuário efetua login por meio de um nome de usuário e senha, o servidor de autorização deve armazenar o token que foi gerado ou metadados sobre o token que foi gerado. Pense nesses metadados como um registro de autorização. Um determinado par de usuário e aplicativo deve ter apenas um token válido, ou autorização, a qualquer momento. Metadados úteis são o id do usuário associado ao token de acesso, o id do aplicativo e a hora em que o token de acesso foi emitido (o que permite a revogação dos tokens de acesso existentes e a emissão de um novo token de acesso). Em cada solicitação de API, valide se o token contém os metadados adequados. Você precisa manter as informações sobre quando cada tokens de acesso foi emitido, para que um usuário possa revogar os tokens de acesso existentes se suas credenciais de conta forem comprometidas e fazer login novamente e começar a usar um novo token de acesso. Isso atualizará o banco de dados com a hora em que o token de acesso foi emitido (a hora de autorização criada). Em cada solicitação de API, verifique se o tempo de emissão do token de acesso é posterior ao tempo de autorização criado.
Outras medidas de segurança incluem não registrar JWTs e exigir um algoritmo de assinatura seguro como o SHA256.
2) Se a verificação da assinatura JWT for o único meio de validar tokens, ou seja, a integridade do segredo do servidor é o ponto de quebra, como os segredos do servidor devem ser gerenciados?
O comprometimento dos segredos do servidor permitiria a um invasor emitir tokens de acesso para qualquer usuário, e o armazenamento de dados do token de acesso na etapa 1 não impediria necessariamente o servidor de aceitar esses tokens de acesso. Por exemplo, digamos que um token de acesso tenha sido emitido para um usuário e, posteriormente, um invasor gere um token de acesso para esse usuário. O tempo de autorização do token de acesso seria válido.
Como Akshay Dhalwala diz, se seu segredo do lado do servidor for comprometido, você terá problemas maiores para lidar, porque isso significa que um invasor comprometeu sua rede interna, seu repositório de código-fonte ou ambos.
No entanto, um sistema para mitigar o dano de um segredo de servidor comprometido e evitar o armazenamento de segredos no código-fonte envolve a rotação de segredo de token usando um serviço de coordenação como https://zookeeper.apache.org. Use um cron job para gerar um segredo de aplicativo a cada poucas horas ou mais (por mais tempo que seus tokens de acesso sejam válidos) e envie o segredo atualizado para o Zookeeper. Em cada servidor de aplicativos que precisa saber o segredo do token, configure um cliente ZK que é atualizado sempre que o valor do nó ZK muda. Armazene um segredo primário e um secundário e, sempre que o segredo do token for alterado, defina o novo segredo do token para o primário e o segredo do token antigo para o secundário. Dessa forma, os tokens válidos existentes ainda serão válidos porque eles serão validados em relação ao segredo secundário. Quando o segredo secundário for substituído pelo segredo primário antigo, todos os tokens de acesso emitidos com o segredo secundário terão expirado de qualquer maneira.
fonte
A IETF tem um RFC em andamento no Grupo de Trabalho oAuth, consulte: https://tools.ietf.org/id/draft-ietf-oauth-jwt-bcp-05.html
fonte