Como arquitetar a autenticação do usuário a partir de aplicativos clientes?


14

Estou desenvolvendo um aplicativo que suporta muitos usuários. O problema é que não consigo descobrir como autenticar o cliente / usuário.

Estou criando um aplicativo como http://quickblox.com/, onde darei credenciais aos meus usuários e eles os usarão para criar N aplicativos nos quais não poderão colocar seu nome de usuário e senha para se autenticar.

Vamos assumir que segue como segue. (Assim como o QuickBlox)

1. O usuário cria uma conta no meu site.
2. O usuário pode criar chaves N API e secretar credenciais. (Para vários aplicativos)
3. O usuário usará essas credenciais em seus aplicativos (Android, iOS, Javascript etc ...) para conversar com minhas APIs REST.
(APIs REST têm acesso de leitura e gravação.)

A minha preocupação?

Os usuários colocam suas credenciais (chave API e chave secreta) nos aplicativos criados, e se alguém obtiver essas chaves e tentar imitar o usuário? (Ao descompilar o APK ou pesquisar diretamente no código JavaScript.

Estou errado em algum lugar?
Estou confuso ao arquitetar esse mecanismo de usuário de três níveis.


O que você parece estar tentando fazer não é possível.
user253751

Existem muitas aplicações por aí que fazem a mesma coisa. Não sei como eles autenticam os usuários.
Alok Patel

Você está tentando autenticar o aplicativo cliente ou o usuário ? Muitos aplicativos autenticam os usuários. Nenhum deles autentica aplicativos cliente de maneira inquebrável.
User253751

Sim, por cliente, quero dizer, quero autenticar apenas o usuário.
Alok Patel

4
Bem, no sistema de autenticação mais comum, basta que o usuário digite o nome de usuário e a senha e os envie com segurança para o servidor que os verifica e (com segurança) retorna um token de sessão e, em seguida, envia o token de sessão cada solicitação futura.
user253751

Respostas:


7

Eu tenho desenvolvido APIs REST nos últimos anos. Você está se preocupando demais. Recentemente, outro usuário deste fórum fez uma pergunta, onde estava preocupado em armazenar pontos de extremidade URI em seu código JavaScript do lado do cliente .

As mesmas regras se aplicam a você e ao desenvolvedor JavaScript. Se você permitir que pessoas de fora integrem sua API, ela terá a mesma visibilidade que um site comum e você deve tratá-la da mesma maneira.

Citação da resposta original:

Quando você está criando um site e não deseja que os usuários façam algo, você não implementa essa funcionalidade ou proíbe certos usuários de usá-lo. Com uma API REST que deve ter terminais públicos, é praticamente a mesma coisa, você deve tratá-la como um site público.

Sua API REST deve ser robusta o suficiente para não permitir operações inválidas, como acesso a dados de um usuário diferente.

Você deve projetar os tokens de acesso ao aplicativo para permitir apenas operações que você deseja permitir. Você pode ter dois tipos de tokens de acesso:

  • token mestre : pode ser usado pelo criador do aplicativo e fornecer mais funcionalidades da sua API,
  • token do aplicativo : o token que realmente seria armazenado nos aplicativos e teria apenas acesso limitado à sua API, apenas às operações que não podem corromper os dados seus ou dos do programador de aplicativos.

Mas o que alguém desconstrói o código-fonte, remove os tokens do aplicativo, descobre quais são os pontos de extremidade públicos e abusa do serviço da Web?

A menos que você esteja gerenciando diretamente o desenvolvimento dos aplicativos que consomem sua API, nada realmente proíbe que as pessoas abusem da API da mesma maneira diretamente do aplicativo.


Boa explicação até agora, eu tenho uma pergunta. Digamos que uma das minhas API seja criada para buscar todos os dados relacionados (A + B) do meu usuário . Meu usuário usa essa API em seu aplicativo para obter apenas o filtered data(B) dele . Agora é possível ou existe alguma solução alternativa para impedir que o terceiro usuário roube todos os dados (A + B) do meu usuário . Isso faz sentido?
Alok Patel

@AlokPatel Não há outra solução alternativa para simplesmente não implementar a funcionalidade. Quando você faz isso, sempre existe o risco de alguém falsificar seu hash de identidade para se parecer com outra pessoa. Programaticamente seu problema não pode ser resolvido. Como eu disse, se você não quiser oferecer alguma funcionalidade, não a implemente. Como os aplicativos provavelmente armazenam o hash de login neles, uma vez que o aplicativo é implantado, sua chave da API também não está disponível ao público.
Andy Andy

Obrigada pelo esclarecimento. Então, o que eu consegui até agora é que as APIs REST são iguais a qualquer site que implantamos e são tão abertas quanto o site. Se não quero que o usuário faça algo, simplesmente não o implico nas minhas APIs REST. Então, o que posso fazer é ter chaves separadas para bibliotecas cliente (Android, iOS, JS) que podem ser comprometidas com menos funcionalidade e chaves diferentes que serão usadas no lado do servidor (PHP, Java, node.js) com funcionalidade estendida. Espero que isso funcione!
Alok Patel

Você pode me explicar seu ponto? A menos que você está gerenciando diretamente o desenvolvimento das aplicações que consomem o seu API
Alok Patel

@AlokPatel O que eu quis dizer é que, no momento, você está com medo de dar a alguém acesso à API, eles podem distribuir o acesso e começar a abusar da API. Se você não tem controle sobre o desenvolvimento dos aplicativos que consomem sua API, eles podem fazer o mesmo, por conta própria.
Andy Andy

5

Seu problema não é tanto técnico como comercial.

Digamos que você tenha sua API, que você vende para seus clientes (os desenvolvedores de aplicativos) por uma taxa fixa de £ 100 por ano, acesso ilimitado.

Bem, obviamente, posso comprar seu serviço por 100 libras e vendê-lo para 10 pessoas por US $ 50 cada. Você não quer isso! então você tenta pensar em uma restrição que permita vender sua API sem que ela seja aberta à arbitragem.

  • Se você apenas restringir o número de aplicativos, um cliente poderá criar um único aplicativo que aceite conexões de outros aplicativos e os repasse.

  • Se você limitar os usuários, novamente, o cliente poderá ocultar os usuários atrás de sua própria autenticação e parecer ser um único usuário.

O que você precisa fazer é repassar o custo de cada chamada da API para o seu cliente. ou seja, cobrar por chamada da API ou definir uma cota de chamadas por ano.

Isso coloca o mesmo problema de arbitragem em seus clientes. Obriga-os a implementar medidas para impedir que seus usuários roubem suas chaves. Nesse caso, oculte sua API por trás da própria API autenticada pelo usuário.


Eu já planejei limitar o uso da minha API com base no número de chamadas da API, para que problemas relacionados aos negócios não sejam da minha preocupação no momento. Estou preocupado apenas com o roubo de chaves.
Alok Patel

Do ponto de vista comercial, o problema é insolúvel. Infelizmente, você também não pode resolvê-lo programaticamente.
Andy Andy

1
O problema do negócio é resolvido cobrando por chamada. Se a chave for roubada, seu cliente perde. Você não. Basta dar o seu cliente uma maneira de cancelar chaves roubadas e dizer seu até que eles tenham uma api intermediária para evitar abusos
Ewan

2

Todas as outras respostas parecem sugerir que o problema de armazenar um segredo em um aplicativo em dispositivos de consumo não é solucionável.

Claro que é.

Dois princípios (os detalhes da implementação seguirão):

  1. Os pontos finais de autenticação reais precisam ser abertos anonimamente ao público.
  2. De alguma forma, o servidor deve estar envolvido na autenticação do cliente e no fornecimento de uma chave de API.

Dado que, se o cliente fizer uma solicitação ao ponto final de autenticação com credenciais e o servidor autenticar, o servidor poderá gerar um token temporário dinâmico (significado temporário com base no tempo). Esse token deve ser lembrado no cliente e enviado com solicitações subsequentes.

Você precisará de um mecanismo para "atualizar" periodicamente o token, ou seja, obter um novo. Basta criar um terminal REST que permita gerar um novo token a partir de um já existente, para evitar a necessidade de se autenticar novamente de credenciais.

Se você estiver tentando evitar que o usuário final se autentique novamente, essa autenticação poderá ser uma configuração única no aplicativo quando ele for instalado.

Essa solução simplesmente evita a necessidade de armazenar um token estático incorporado no binário do aplicativo. Um token é gerado imediatamente pelo servidor apenas em resposta a uma autenticação bem-sucedida. Para que um usuário mal-intencionado inspecione seu aplicativo e tente obter acesso não autorizado à API, ele ainda precisará se autenticar como qualquer outra pessoa.


Solução interessante, apesar de depender principalmente de como os clientes do OP codificarão seu aplicativo. Dito isto, a abordagem proposta pode permitir armazenar o lado do servidor de chaves da API e, após uma autenticação bem-sucedida de seus próprios usuários, pode encaminhar a chave temporária para o aplicativo real usar. Dessa forma, nada é armazenado no próprio aplicativo.
Newtopian 8/08/16

Isso é verdade para todas as APIs. Se um consumidor de API não o consumir corretamente, as coisas podem não funcionar. Isso teria que ser uma parte documentada da API.
Brandon

A propósito, existem padrões para ajudar a facilitar isso, como o OAuth.
Brandon

0

Se você tiver chaves únicas por aplicativo, poderá usá-las apenas durante uma autenticação de conexão inicial, iniciada pelo cliente, após a qual você alterna para um token de autenticação exclusivo por aplicativo rolante.

Seu servidor altera (rola) o token para cada aplicativo cliente de tempos em tempos (periodicamente mais / menos algum atraso aleatório difuso / aleatório, por exemplo). O token rotativo é conhecido apenas pelo servidor e pelo cliente autenticado.

Os novos tokens são devolvidos às respostas regulares. Sempre que a resposta contém um novo token, o aplicativo receptor precisa mudar para usá-lo em solicitações subsequentes.

Sempre que a troca com um cliente ficar fora de sincronia (algum tipo de erro de protocolo), o servidor solicitará a re-autenticação (por meio de uma resposta de erro à próxima solicitação do cliente, ou suportará a resposta válida à solicitação do cliente, por exemplo).

A autenticação inicial iniciada pelos clientes enquanto um token rotativo é usado ativamente deve ser considerada com suspeita - pode muito bem ser uma tentativa de imitação. Eu permitiria apenas se a troca ficasse ociosa por um intervalo maior que o esperado, o que poderia, por exemplo, ser causado por uma interrupção / reinicialização do cliente com uma nova instância que não possui o token de rolagem.

Seria ainda melhor persistir o token no lado do cliente, para que um cliente reiniciado possa continuar de onde seu antecessor saiu - estreitando significativamente a abertura para imitar.

Esse esquema tornaria a imitação pelo menos bastante difícil - o cliente imitador precisaria prever exatamente a janela quando o cliente autorizado parar de enviar solicitações por tempo suficiente para o servidor decidir que não há problema em aceitar uma nova autenticação de cliente com a chave prescrita. Tais solicitações fora da janela permitida podem ser usadas como detecção de tentativas de imitação e, possivelmente, iniciar algumas contramedidas (lista negra de IP, etc.).


0

Pelo que sei, o que você mencionou é a única maneira de fazer isso. O aplicativo que armazena a chave é definitivamente um risco, mas existem várias maneiras de contorná-la. Você sempre pode usar o keystore para armazenar a chave, em vez de codificar, forçando assim um login único.

Além disso, considere vincular uma chave a um cliente; assim, se alguém imitar, deverá ter uma camada de segurança para verificar se o cliente, a chave e os agentes do usuário bloqueiam a solicitação imediatamente. Tenha um caso de uso para emitir novamente ou garantir novamente que as chaves não sejam imitadas.


O que é uma solução alternativa para JavaScript? Como arquitetar para isso?
Alok Patel

Suponho que você esteja falando sobre a criação de um aplicativo híbrido. Se for esse o caso, definitivamente você poderá criar um plug-in para armazenar as chaves. Se você estiver indo para hospedar o aplicativo e falar sobre solução de web móvel, depois pensar em processar a página usando um recipiente e você pode contar com um token de sessão sugerido anteriormente
Arun

Não, não é o caso. Eu seria o terceiro principal provedor de API, meus usuários usarão meu serviço de API em seus aplicativos. Pode ser Android, iOS, JavaScript etc. #
Alok Patel

0

Se você depende de fornecer tokens de autorização para seus clientes colocarem seus aplicativos, sempre será teoricamente possível para alguém fazer engenharia reversa do aplicativo e extraí-los. Para evitar isso, você precisará de um mecanismo que não exija um segredo no aplicativo cliente. Isso é complicado. Eu posso sugerir algumas opções para você pensar.

Você tem um problema depois de fornecer as credenciais, não tem controle sobre a segurança com que elas são armazenadas. Além disso, se você exigir que o usuário envie as credenciais para você, alguém poderá MITM sua conexão e roubar os tokens diretamente sem se preocupar em fazer engenharia reversa no aplicativo.

Uma maneira de tornar mais difícil extrair seu token de autorização é ofuscá-lo. Isso apenas eleva a fasquia, mas não torna impossível, e para fazer isso, você teria que manter o controle do segredo. Você pode implementar uma biblioteca que contém as informações secretas e é específica para cada cliente. Você pode usar a biblioteca para se comunicar com seus servidores e talvez nem precise informar ao usuário as informações secretas, elas podem ser incorporadas à biblioteca. Isso não resolve o problema de alguém fazer engenharia reversa na sua biblioteca, mas coloca você no controle do nível de ofuscação. Uma desvantagem é que, uma vez que uma pessoa tenha quebrado a ofuscação na biblioteca, ela poderá atacar qualquer biblioteca sua, a menos que você escreva um código que torne cada biblioteca significativamente diferente. Isso introduz seu próprio conjunto de problemas.

Isso pode se afastar um pouco do escopo da sua pergunta, mas está relacionado à segurança do seu token, por isso vou mencioná-lo. Para impedir o roubo trivial do token pela conexão, você provavelmente não deseja enviar o token diretamente; em vez disso, você pode assinar o tráfego usando uma função HMAC. Você pode verificar a validade da mensagem calculando o HMAC da mensagem no servidor e comparando-o com o HMAC enviado do cliente. Você usaria o token como a chave para a função HMAC, para que apenas alguém que conheça o token possa assinar tráfego. Isso é melhor para a segurança do seu token, porque você nunca o envia diretamente para o servidor, para que não possa ser interceptado e roubado diretamente. Para obter mais informações sobre o HMACS, consulte esta pergunta: /security/20129/how-and-when-do-i-use-hmac/20301

Nenhuma solução de segurança é inexpugnável; você precisa decidir quanto vai custar para implementar versus a probabilidade e o custo de ser comprometido.


Não consigo criar uma biblioteca e colocar segredos nela, serão diferentes para cada um dos meus usuários.
Alok Patel

Para esclarecer, eu estava sugerindo que você pudesse criar uma biblioteca personalizada para cada um de seus usuários e ocultar seus segredos individuais dentro da biblioteca. Você precisaria escrever um sistema para gerar automaticamente as bibliotecas sob demanda para cada novo usuário, o que pode ser mais trabalhoso do que você deseja.
ThePragmatist

0

Citando a si mesmo:

Não consigo descobrir como autenticar o cliente / usuário ... no qual eles não podem colocar seu nome de usuário e senha para serem autenticados.

Agora isso é um pouco contraditório, não é? ;)

Como outros disseram, você não pode. Se um aplicativo usa uma chave de API, pode-se descompilar como você pede para obter a (s) chave (s) e usá-la também.

Além de exigir autenticação adequada adicional do usuário, você só pode limitar o dano:

  • IPs da lista de permissões / lista negra
  • estrangulamento
  • detecção de "atividade incomum" e sua origem
  • renovação fácil da chave
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.