Autenticação baseada em token usando tokens de acesso e atualização

8

Estou implementando um sistema de autenticação baseado em token para uma API REST usando um token de acesso de curta duração e um token de atualização de longa duração. Esta é uma visão geral abstrata dos pontos de extremidade da API relevantes (o HTTPS é imposto a todos os pontos de extremidade):

Pontos finais:

POST /register/
POST /login/
POST /logout/
POST /password/change/

Implementação:

POST /register/:

  • Solicitação: o cliente envia nome de usuário, email e senha em JSON.
  • Ações do servidor:
    1. Valida a entrada, cria usuário no banco de dados (armazena ID do usuário, nome de usuário, email e hash da senha).
    2. Cria um token de acesso de curta duração no formato JWT (contém o ID do usuário, a data de emissão e a data de validade).
    3. Cria um token de atualização de longa duração como uma string UUID e o armazena no banco de dados (armazena o ID do usuário e o token de atualização).
  • Resposta: O servidor retorna o token de acesso e o token de atualização no JSON.

POST /login/:

  • Solicitação: o cliente envia nome de usuário e senha em JSON.
  • Ações do servidor:
    1. Valida a entrada, verifica se as credenciais são válidas verificando o banco de dados.
    2. Se as credenciais forem válidas, cria um token de acesso de curta duração e um token de atualização de longa duração, conforme mencionado anteriormente.
  • Resposta: O mesmo que /register/, retorna o token de acesso e o token de atualização no JSON.

POST /logout/:

  • Solicitação: o cliente envia o token de atualização no Authorizationcabeçalho como Bearertoken.
  • Ações do servidor:
    1. Valida o token de atualização verificando o banco de dados do token de atualização.
    2. Remove o token de atualização do banco de dados.
      Nota: Isso deixa o token de acesso válido, mas como ele terá vida curta (1 hora ou mais, acho que deve estar bom).
  • Resposta: Retorna se a solicitação de logout foi processada com sucesso em JSON.

POST /password/change/:

  • Solicitação: o cliente envia o token de acesso no Authorizationcabeçalho como Bearertoken e também envia a senha antiga e a nova senha no JSON por HTTPS.
  • Ações do servidor:
    1. Decodifica o token de acesso para recuperar o usuário e verifica a senha antiga do usuário no banco de dados.
    2. Define o hash da senha do usuário no banco de dados como o hash da nova senha.
    3. Remove todos os tokens de atualização associados ao usuário no banco de dados do token de atualização para efetivamente desconectar as sessões existentes (deixa válidos os tokens de acesso de curta duração).
  • Resposta: Retorna se a solicitação de alteração de senha foi processada com sucesso em JSON.

Questões:

  1. Essa abordagem é segura? Especificamente:
    • O envio do nome de usuário e da senha através do JSON é seguro se for feito por HTTPS? Como impediria que domínios não autorizados fizessem chamadas para este endpoint? Além disso, como eu evitaria logins programáticos?
    • Os tokens de atualização devem ser divididos em hash antes de armazená-los no banco de dados ou estou sendo paranóico?
  2. Se o cliente fosse um navegador da Web, como eu armazenaria com segurança o token de atualização no cliente?
    • Uma idéia que tenho para armazenar o token de atualização é: quando o usuário efetua login, além de enviar o token de atualização ao cliente, o servidor armazena o token em um HttpOnlycookie com um securesinalizador. A autorização ainda será feita por meio do Authorizationcabeçalho, mas quando o cliente for carregado inicialmente, ele poderá enviar uma GETsolicitação para um terminal que verifique se o cookie contém um token de atualização válido e, se houver, devolva-o ao usuário no JSON. Em outras palavras, a única vez que o cookie será realmente usado é retornar o token de atualização dentro do cookie para o cliente. Essa abordagem é segura? Acho que isso impedirá o CSRF, pois não há efeitos colaterais ao solicitar o token de atualização do cookie, mas existe outra maneira de um invasor interceptar o token de atualização (assumindo HTTPS)?
Kootling
fonte
2
Por que não usar o Open ID Connect em vez de tentar implementar seu próprio mecanismo de segurança de logon?
precisa
Não quero ter que lidar com um servidor adicional para atuar como o provedor de conexão OpenID. Além disso, eu preferiria ter controle total sobre o fluxo de autenticação para poder identificar facilmente erros e vulnerabilidades de segurança.
Kootling
2
Eu não acho que isso exija um servidor extra se você apenas o usar. Além disso, fazer sua própria implementação com seu próprio protocolo tem seus próprios riscos de segurança, talvez mais do que usar a abordagem padrão emergente, mas quem sabe com certeza?
Erik Eidt

Respostas:

2

Essa abordagem é segura? Especificamente:

  • O envio do nome de usuário e da senha através do JSON é seguro se for feito por HTTPS?

Sim. Cabeçalhos, parâmetros de solicitação e corpo da solicitação são criptografados durante a comunicação.

Uma vez no lado do servidor, não registre o corpo da solicitação :-)

  • Como impediria que domínios não autorizados fizessem chamadas para este endpoint?

Você não pode. Basicamente, quando a API está na WWW, é automaticamente exposta a todo tipo de malícia. O melhor que você pode fazer é estar preparado e estar ciente das ameaças. Pelo menos sobre aqueles que lhe dizem respeito. Dê uma olhada aqui .

Uma possível abordagem para o problema poderia estar implementando (ou contratando) um API Manager .

Os gerentes de API locais podem reduzir a superfície de ataque porque todos os pontos de extremidade atrás da AM não são necessariamente públicos.

Você pode obter o mesmo resultado com alguns produtos na nuvem, mas eles são absurdamente caros para o mainstream.

De qualquer forma, os pontos de extremidade do Gerenciamento de API permanecerão expostos a ataques.

  • Além disso, como eu evitaria logins programáticos?

Se por logins programáticos você quer dizer ataques por força bruta, um limite (número máximo de solicitações permitidas por segundo) e uma lista negra devem ser suficientes para impedir a insistência do invasor. Para mais informações, dê uma olhada aqui .

Muitos dos gerentes de API fornecem configurações e limites de taxa de API prontos para uso .

Se você conhece o Google API Console, pode adivinhar o que um API Manager pode fazer.

  • Os tokens de atualização devem ser divididos em hash antes de armazená-los no banco de dados ou estou sendo paranóico?

Se o token de atualização é um UUID simples ou qualquer outra coisa, não gosto de expor esse tipo de detalhe de implementação. Então, eu sugiro hash. Para mim, quanto mais opacos forem os detalhes de implementação da camada de segurança, melhor.

Em relação à segurança do JWT, dê uma olhada aqui .

  • Se o cliente fosse um navegador da Web, como eu armazenaria com segurança o token de atualização no cliente?

Você pode estar interessado em JSON Web Token (JWT) - armazenamento no lado do cliente .

Laiv
fonte
Em relação aos logins programáticos, quero impedir que alguém crie seu próprio front-end para a API (embora, como você afirmou, isso seja praticamente impossível). Além disso, os tokens de atualização (que são UUIDs, não JWTs) atualmente são armazenados como texto sem formatação no banco de dados, então eu definitivamente devo fazer o hash deles, certo? Em relação ao armazenamento de token do lado do cliente, desejo armazenar os tokens de atualização e persistir por sessões, para sessionStorageque não funcionem. Além disso, localStoragee document.cookieparecem inseguros, pois podem ser acessados ​​por JavaScript. Minha abordagem faz sentido ou é potencialmente insegura?
Kootling
1. Sim, eu armazenaria um token de hash em vez do texto sem formatação. Apenas por tornar o conteúdo da tabela opaco para outros processos. 2. Se eu tiver que escolher entre localStorage e cookies, eu escolho cookies. No entanto, eu faria primeiro uma pequena pesquisa sobre suas vulnerabilidades. Além disso, considere adicionar à segurança um mecanismo para invalidar os tokens manualmente.
LAIV
Por "cookies", você quer dizer document.cookieum cookie HttpOnly com um sinalizador seguro, semelhante à abordagem mencionada acima?
Kootling
O sinalizador HttpOnly e seguro não impedem o XST e o XSRF. Mas torna o cookie seguro para ataques XSS, que considero básico. Então, sim, HttpOnly e sinalizador seguro. Basta lembrar que você não terá acesso ao cookie do Javascript
LAIV
De qualquer forma, recomendo que você faça check-out dos diferentes projetos OWASP relacionados a aplicativos da Web, API, REST e JWT. Você encontrará muito mais informações do que as que posso fornecer aqui.
LAIV