Como proteger uma API REST apenas para aplicativos móveis confiáveis

96

Como garantir que minha API REST responda apenas a solicitações geradas por clientes confiáveis, no meu caso, meus próprios aplicativos móveis? Quero impedir que solicitações indesejadas venham de outras fontes. Não quero que os usuários preencham uma chave serial ou o que seja, isso deve acontecer nos bastidores, na instalação e sem a necessidade de interação do usuário.

Até onde eu sei, o HTTPS é apenas para validar o servidor com o qual você está se comunicando e quem ele diz que é. Claro que vou usar HTTPS para criptografar os dados.

Existe uma maneira de conseguir isso?

Atualização: o usuário pode executar ações somente leitura, que não exigem que o usuário esteja conectado, mas também pode executar ações de gravação, que exigem que o usuário esteja conectado (Autenticação por token de acesso). Nos dois casos, quero que a API responda a solicitações provenientes apenas de aplicativos móveis confiáveis.

A API também será usada para registrar uma nova conta através do aplicativo móvel.

Atualização 2: Parece que existem várias respostas para isso, mas sinceramente não sei qual sinalizar como resposta. Alguns dizem que isso pode ser feito, outros dizem que não.

Supercell
fonte
HTTPS usa SSL (e TLS). SSL / TLS pode ser usado com autenticação de cliente.
Atk
Você quer dizer certificados SSL do lado do cliente? Acho que é isso que estou procurando, exceto que não faço ideia se isso é possível em aplicativos móveis (Android e iOS)? Onde o certificado do cliente seria armazenado? Armazenamento de dispositivo, memória?
Supercell
O SSL só certifica o dispositivo móvel, NÃO o aplicativo móvel.
Idiotas
@Supercell: vou adicionar uma resposta
atk

Respostas:

48

Você não pode.

Você nunca pode verificar uma entidade, qualquer entidade , seja uma pessoa, cliente de hardware ou cliente de software. Você só pode verificar se o que eles estão dizendo está correto e assumir honestidade .

Por exemplo, como o Google sabe que estou fazendo login na minha conta do Gmail? Eles simplesmente me pedem um nome de usuário e uma senha, verificam isso e assumem honestidade porque quem mais teria essas informações? Em algum momento, o Google decidiu que isso não era suficiente e adicionou uma verificação comportamental (procurando um comportamento estranho), mas que ainda depende da pessoa para fazer o comportamento e depois a validação do comportamento .

Isso é exatamente o mesmo com a validação do cliente. Você só pode validar o comportamento do cliente, mas não o próprio cliente.

Portanto, com o SSL, você pode verificar se o Cliente possui um certificado válido ou não. Assim, basta instalar o aplicativo, obter o certificado e executar todo o novo código.

Então a pergunta é: por que isso é tão crítico? Se essa é uma preocupação real, eu questionaria sua escolha de um cliente gordo. Talvez você deva usar um aplicativo da Web (para não precisar expor sua API).

Consulte também: Derrotando a validação de certificado SSL para aplicativos Android

e: Quão seguros são os certificados SSL do cliente em um aplicativo móvel?

Idiotas
fonte
1
Eu usei certificados de cliente no hardware em que o certificado foi armazenado em uma unidade criptografada pelo sistema operacional. Mas mesmo lá, ninguém acreditava que fosse à prova de falhas. O objetivo era apenas dificultar os usuários casuais.
Steven Burnap
1
@ Moron: um webapp resolveria isso, mas achamos que os usuários provavelmente usarão o aplicativo nativo do que um webapp (por favor, corrija-me se nossa suposição estiver errada). A razão pela qual isso é tão crítico é porque a API fornece ao usuário acesso a partes de nosso banco de dados, que contém muitos dados que reunimos durante meses de trabalho. São dados que outras empresas ou usuários podem usar facilmente para seus próprios fins. Sem proteger os clientes, não saberíamos quem o estava usando (contra nós).
Supercell
6
Um webapp não resolve o problema. É bastante trivial modificar qualquer aplicativo da Web do lado do cliente e fazê-lo do jeito que você quiser.
Steven Burnap
5
@Supercell Você não pode mostrar os dados de alguém e impedi-los de compartilhá-los. Se você não quer que alguns tenham dados, não os dê (mostre) a eles.
Idiotas
Eu concordo, mas por outra razão. Você pode se você tiver controle sobre os dispositivos, como o rsa en.wikipedia.org/wiki/SecurID . Mas os celulares não são algo que possa ser controlado (eles podem aceitar anexos como uma chave de plug-in ou algo assim).
Imel96
31

Tenho certeza de que você se sente à vontade para lidar com logins de usuários e com comunicações via SSL; portanto, vou me concentrar no que acho a parte mais interessante da pergunta: como garantir que suas ações somente leitura - que não exigem que o usuário seja autenticado - são aceitos apenas em seus próprios aplicativos clientes?

Antes de mais nada, existe o lado negativo que o fNek sugeriu em uma resposta anterior - seus aplicativos clientes estão nas mãos de usuários potencialmente hostis. Eles podem ser examinados, suas comunicações inspecionadas, seu código desmontado. Nada do que vou sugerir permitirá garantir que alguém não faça engenharia reversa no seu cliente e abuse da API REST. Mas deve colocar uma barreira na frente de qualquer tentativa casual.

De qualquer forma, uma abordagem comum é:

  • O cliente contém um segredo
  • Ao fazer uma solicitação, concatena os parâmetros da solicitação com os segredos e faz o hash do resultado
  • Esse hash é enviado com a solicitação e verificado pelo servidor

Por exemplo, imagine um GETpedido de/products/widgets

Digamos que o segredo do cliente seja "OH_HAI_I_IZ_SECRET"

Concatene o verbo HTTP, a URL e o segredo:

GET/products/widgetsOH_HAI_I_IZ_SECRET

E pegue um hash SHA-1 disso:

4156023ce06aff06777bef3ecaf6d7fdb6ca4e02

Em seguida, envie-o, para que a solicitação seja:

GET /products/widgets?hash=4156023ce06aff06777bef3ecaf6d7fdb6ca4e02

Por fim, para impedir que alguém reproduza solicitações individuais pelo menos, use também um carimbo de data e hora e adicione-o aos parâmetros e ao hash. por exemplo, agora, no horário Unix, é 1384987891. Adicione isso à concatenação:

GET/products/widgetsOH_HAI_I_IZ_SECRET1384987891

Hash que:

2774561d4e9eb37994d6d71e4f396b85af6cacd1

E envia:

GET /products/widgets?time=1384987891&hash=2774561d4e9eb37994d6d71e4f396b85af6cacd1

O servidor verificará o hash e também verificará se o registro de data e hora está atualizado (por exemplo, dentro de 5 minutos para permitir que os relógios não estejam perfeitamente sincronizados)

Aviso! Como você está falando de aplicativos para dispositivos móveis, há um risco definido de que o telefone de alguém tenha o relógio errado. Ou fuso horário errado. Ou alguma coisa. A adição de tempo ao hash provavelmente quebrará alguns usuários legítimos; portanto, use essa idéia com cautela.

Carson63000
fonte
6
esse mecanismo de hash pode ser entendido por qualquer programador quando ele desmontar o apk.
Punith Raj
8
@PunithRaj exatamente, eu cobri isso no segundo parágrafo. "Nada do que vou sugerir permitirá que você garanta que alguém não faça engenharia reversa no seu cliente e abuse da API REST. Mas isso deve colocar uma barreira na frente de qualquer tentativa casual".
Carson63000
pelo aviso, estou usando o UTC no servidor e no celular, isso resolve o problema, certo?
shareef 13/04
@ Carson63000 - então, existe uma solução concreta? especialmente para a API de registro de usuários, que precisa ser aberta publicamente (o usuário precisa se registrar antes de poder fazer login, na Web ou em um aplicativo móvel) e pode ser direcionado por bots para criar milhares de usuários falsos.
Tohid
17

Para qualquer pessoa interessada, no Android, você PODE verificar se a solicitação que recebeu foi enviada do seu aplicativo.

Em resumo, quando você faz o upload do seu aplicativo para o Google, assina-o, com uma chave exclusiva conhecida apenas por você (e pelo Google).

O processo de verificação é (ish) assim:

  1. seu aplicativo acessa o google e solicita o token de autenticação
  2. seu aplicativo envia o token com segurança para seu back-end
    1. seu back-end acessa o Google e verifica o token de autenticação obtido do seu aplicativo.
    2. seu back-end verifica se a chave exclusiva que seu aplicativo assinou corresponde. Caso contrário, isso significa que não era seu aplicativo ...

o blog completo que explica e como implementá-lo pode ser encontrado aqui: http://android-developers.blogspot.co.il/2013/01/verifying-back-end-calls-from-android.html

ndori
fonte
1
Boa resposta, no entanto, um usuário mal-intencionado ainda pode falsificar um aplicativo com esforço suficiente. mas nada é realmente seguro, não é uma questão de se, apenas uma questão de quando
Mateos
1
Para iOS, existe esta opção: link As APIs DeviceCheck também permitem verificar se o token recebido é proveniente de um autêntico dispositivo Apple no qual o aplicativo foi baixado
Iwaz
Ele requer contas (e-mails)
user25
5

Ok, vale a pena mencionar antes de começar que, para a maioria das aplicações, isso é um exagero. Para a maioria dos casos de uso, simplesmente ter um único certificado e / ou token válido é mais que suficiente. Se envolver algo difícil como descompilar o aplicativo, mesmo a maioria dos hackers não se incomodará, a menos que você forneça alguns dados muito valiosos. Mas ei, qual é a graça nessa resposta?

Então, o que você pode fazer é configurar a criptografia assimétrica, como as assinaturas digitais usadas para assinar programas. Cada aplicativo pode ter um certificado individual emitido por uma única CA e verificado quando o usuário se conecta. (no primeiro registro ou na primeira instalação) Quando esse certificado é autenticado, você pode proteger ainda mais seu aplicativo registrando esse certificado como válido para um determinado identificador de dispositivo (como o Android ID )

Tom Squires
fonte
5

Como o @Morons mencionado em sua resposta, é muito difícil verificar a entidade na outra extremidade da conexão.

A maneira mais simples de fornecer algum nível de autenticidade é fazer com que o servidor verifique algum segredo que somente a entidade real saberia. Para um usuário, esse pode ser um nome de usuário e uma senha. Para um software em que não há usuário, você pode incorporar um segredo.

O problema com essas abordagens é que você precisa confiar no cliente. Se alguém reverter seu aplicativo ou roubar sua senha, ele poderá fingir ser você.

Você pode tomar medidas para dificultar a extração das informações secretas, ofuscando-as no executável. Ferramentas como o ProGuard, que é um ofuscador para Java, podem ajudar com isso. Não sei muito sobre ofuscação em outras linguagens, mas provavelmente existem ferramentas semelhantes. O uso de uma conexão TLS ajuda a impedir que as pessoas bisbilhotem seu tráfego, mas não impede um ataque MITM. A fixação pode ajudar a resolver esse problema.

Eu trabalho para uma empresa chamada CriticalBlue (divulgação completa!) Que possui um produto chamado Approov que tenta solucionar esse problema de confiança. Atualmente, ele funciona para Android / iOS e fornece um mecanismo para nossos servidores verificarem a integridade do aplicativo cliente. Isso é feito fazendo com que o cliente calcule uma resposta a um desafio aleatório. O cliente precisa calcular a resposta usando atributos do pacote de aplicativos instalados que são difíceis de falsificar e incluem alguns sofisticados mecanismos anti-adulteração.

Ele retorna um token que você pode enviar como prova de autenticidade para sua API.

A diferença importante com essa abordagem é que, embora seja possível desativar a verificação de autenticidade no cliente, se você o fizer, não obterá o token de autenticação, precisará verificar seu aplicativo com o servidor. A biblioteca também está fortemente acoplada às características do executável em que está, portanto, seria muito difícil incorporá-lo em um aplicativo falso e fazê-lo funcionar.

Existe uma análise de custo / benefício que qualquer desenvolvedor de API deve fazer para decidir qual a probabilidade de alguém tentar invadir sua API e qual o custo. Uma simples verificação secreta no aplicativo evita ataques triviais, mas proteger-se contra um invasor mais determinado é provavelmente consideravelmente mais complicado e potencialmente caro.

ThePragmatist
fonte
0

O SSL protegerá o canal de comunicação.

O login bem-sucedido emitirá um token de autenticação pela conexão criptografada.

O token de autenticação será passado para sua API REST em todas as solicitações subsequentes.

CodeART
fonte
1
Eu adicionei algumas informações adicionais. Eu estava planejando fazer autenticação como você mencionou, com um token de acesso. A API REST não exige que o usuário esteja conectado, apenas para ações específicas. Nos dois casos, os clientes precisam ser assinados / confiáveis.
Supercell
Não sei muito sobre desenvolvimento móvel nativo, mas seu aplicativo móvel pode fornecer o número de celular à API REST? Quando instalo aplicativos no meu telefone Android, muitas vezes me pedem para conceder determinadas permissões ao aplicativo. Se você puder enviar um número de celular com todas as solicitações por uma conexão segura, poderá rejeitar todas as solicitações com número de celular desconhecido. Só de pensar em voz alta aqui ...
CodeART
Isso poderia funcionar, mas exigiria que o usuário estivesse conectado e o número vinculado à conta do usuário. Caso contrário, o servidor não terá nada para verificar o número.
Supercell
Como você instalará seu aplicativo móvel?
CodeART 21/11
Eles estarão disponíveis nas lojas de aplicativos (Google, Apple, Microsoft)
Supercell
0

Não seria muito seguro, mas você poderia adicionar algum tipo de código secreto ou até uma assinatura digital. Desvantagem: ele deve ser incluído no aplicativo, o que facilita a sua obtenção se você souber o que faz.

fNek
fonte
0

Até onde eu sei, o HTTPS é apenas para validar o servidor com o qual você está se comunicando e quem ele diz que é.

De fato, você pode usar o SSL para autenticar o cliente e o servidor. Ou, dito de forma diferente, "sim, você pode usar certificados de cliente".

Você precisará ...

  • observe a biblioteca SSL que você está usando para determinar como especificar certificados de cliente no dispositivo móvel,
  • escreva código ou configure seu servidor HTTPS para que ele aceite apenas conexões de clientes registrados e confiáveis.
  • crie um mecanismo para adicionar certificados de cliente confiáveis ​​ao seu servidor
  • crie um mecanismo para remover certificados de cliente não mais confiáveis ​​do seu servidor

Você pode fazer com que o aplicativo móvel armazene o certificado onde quiser. Como você deseja autenticação específica do aplicativo, considere armazenar o certificado em um local protegido do disco (no Android, você pode criar uma tabela "config" no banco de dados SQLite e uma linha para o seu certificado e outra para a sua chave privada) .

atk
fonte