Estou projetando um serviço da web RESTful que precisa ser acessado pelos usuários, mas também por outros serviços e aplicativos da web. Todas as solicitações recebidas precisam ser autenticadas. Toda a comunicação ocorre por HTTPS. A autenticação do usuário vai funcionar com base em um token de autenticação, adquirido pelo POST do nome de usuário e senha (em uma conexão SSL) para um recurso / sessão fornecido pelo serviço.
No caso de clientes de serviço da Web, não há usuário final por trás do serviço de cliente. As solicitações são iniciadas por tarefas agendadas, eventos ou algumas outras operações do computador. A lista de serviços de conexão é conhecida de antemão (obviamente, eu acho). Como devo autenticar essas solicitações provenientes de outros serviços (web)? Quero que o processo de autenticação seja o mais fácil possível de implementar para esses serviços, mas não à custa da segurança. Qual seria o padrão e as melhores práticas para um cenário como este?
Opções que posso pensar (ou que me foram sugeridas):
Faça com que os serviços ao cliente recorram a um nome de usuário e senha "falsos" e os autentique da mesma forma que os usuários. Eu não gosto dessa opção - ela simplesmente não parece certa.
Atribua um ID de aplicativo permanente para o serviço do cliente, possivelmente uma chave de aplicativo também. Pelo que entendi, isso é o mesmo que ter nome de usuário + senha. Com essa id e chave, posso autenticar cada solicitação ou criar um token de autenticação para autenticar outras solicitações. De qualquer forma, não gosto dessa opção, porque qualquer pessoa que conseguir o ID e a chave do aplicativo pode se passar pelo cliente.
Eu poderia adicionar uma verificação de endereço IP à opção anterior. Isso tornaria mais difícil realizar solicitações falsas.
Certificados de cliente. Configurar minha própria autoridade de certificação, criar certificado raiz e criar certificados de cliente para os serviços de cliente. No entanto, algumas questões vêm à mente: a) como eu ainda permito que os usuários se autentiquem sem certificados eb) quão complicado é este cenário de implementar do ponto de vista do serviço ao cliente?
Outra coisa - deve haver outras soluções por aí?
Meu serviço estaria rodando em Java, mas eu deliberadamente deixei de fora informações sobre em qual framework específico ele seria construído, porque estou mais interessado nos princípios básicos e não tanto nos detalhes de implementação - presumo que a melhor solução para isso será ser possível implementar independentemente da estrutura subjacente. No entanto, sou um pouco inexperiente neste assunto, portanto, dicas e exemplos concretos sobre a implementação real (como bibliotecas de terceiros úteis, artigos, etc.) também serão muito apreciados.
Respostas:
Qualquer solução para esse problema se resume a um segredo compartilhado. Também não gosto da opção de nome de usuário e senha embutidos em código, mas tem a vantagem de ser bastante simples. O certificado do cliente também é bom, mas é realmente muito diferente? Há um certificado no servidor e outro no cliente. Sua principal vantagem é que é mais difícil usar a força bruta. Espero que você tenha outras proteções para se proteger contra isso.
Não acho que seu ponto A para a solução de certificado de cliente seja difícil de resolver. Você acabou de usar um galho.
if (client side certificat) { check it } else { http basic auth }
Não sou um especialista em java e nunca trabalhei com ele para fazer certificados do lado do cliente. No entanto, um rápido Google nos leva a este tutorial que parece exatamente o seu caminho.Apesar de toda essa discussão sobre "o que é melhor", deixe-me apenas salientar que há outra filosofia que diz: "menos código, menos inteligência é melhor". (Eu pessoalmente mantenho essa filosofia). A solução de certificado de cliente parece muito código.
Sei que você expressou dúvidas sobre o OAuth, mas a proposta do OAuth2 inclui uma solução para seu problema chamada " tokens de portador ", que deve ser usado em conjunto com SSL. Acho que, por uma questão de simplicidade, eu escolheria o usuário / senha codificado (um por aplicativo para que possam ser revogados individualmente) ou os tokens de portador muito semelhantes.
fonte
Depois de ler sua pergunta, eu diria, gere token especial para fazer a solicitação necessária. Este token viverá em um tempo específico (digamos, em um dia).
Aqui está um exemplo de para gerar token de autenticação:
por exemplo: 3 de junho de 2011
em seguida, concatene com a senha do usuário, por exemplo "my4wesomeP4ssword!"
Em seguida, faça MD5 dessa string:
Quando você chama uma solicitação, sempre use este token,
Esse token é sempre único todos os dias, então acho que esse tipo de proteção é mais do que suficiente para sempre proteger nosso serviço.
Espero que ajude
:)
fonte
Existem várias abordagens diferentes que você pode adotar.
Os puristas RESTful vão querer que você use a autenticação BASIC e envie credenciais em cada solicitação. Seu raciocínio é que ninguém está armazenando nenhum estado.
O serviço de cliente pode armazenar um cookie, que mantém um ID de sessão. Pessoalmente, não acho isso tão ofensivo quanto alguns dos puristas de quem ouço - pode ser caro autenticar continuamente. Parece que você não gosta muito dessa ideia.
Pela sua descrição, realmente parece que você pode estar interessado no OAuth2. Minha experiência até agora, pelo que tenho visto, é que é meio confuso e meio sangrento. Existem implementações por aí, mas são poucas e raras. Em Java, eu entendo que ele foi integrado aos módulos de segurança do Spring3 . (O tutorial deles está bem escrito.) Estou esperando para ver se haverá uma extensão no Restlet , mas até agora, embora tenha sido proposta e possa estar na incubadora, ainda não foi totalmente incorporada.
fonte
Acredito na abordagem:
é bastante padrão, independentemente de como você implementa e outros detalhes técnicos específicos.
Se você realmente quiser ir além, talvez possa considerar a chave https do cliente em um estado temporariamente inválido até que as credenciais sejam validadas, limitar as informações se nunca forem validadas e conceder acesso quando forem validadas, novamente com base na expiração.
Espero que isto ajude
fonte
No que diz respeito à abordagem de certificado de cliente, não seria terrivelmente difícil de implementar e, ao mesmo tempo, permitir a entrada de usuários sem certificados de cliente.
Se você de fato criar sua própria Autoridade de Certificação autoassinada e emitir certificados de cliente para cada serviço do cliente, terá uma maneira fácil de autenticar esses serviços.
Dependendo do servidor da web que você está usando, deve haver um método para especificar a autenticação do cliente que aceitará um certificado de cliente, mas não exigirá um. Por exemplo, no Tomcat ao especificar seu conector https, você pode definir 'clientAuth = want', em vez de 'true' ou 'false'. Em seguida, certifique-se de adicionar seu certificado CA autoassinado ao seu armazenamento confiável (por padrão, o arquivo cacerts no JRE que você está usando, a menos que tenha especificado outro arquivo na configuração do servidor da web), de modo que os únicos certificados confiáveis seriam aqueles emitidos fora de seu CA auto-assinado.
No lado do servidor, você só permitiria o acesso aos serviços que deseja proteger se pudesse recuperar um certificado de cliente da solicitação (não nulo) e passaria em qualquer verificação de DN se preferir qualquer segurança extra. Para os usuários sem certificados de cliente, eles ainda poderão acessar seus serviços, mas simplesmente não terão certificados presentes na solicitação.
Em minha opinião, esta é a maneira mais 'segura', mas certamente tem sua curva de aprendizado e sobrecarga, portanto pode não ser necessariamente a melhor solução para suas necessidades.
fonte
5. Outra coisa - deve haver outras soluções por aí?
Você está certo, existe! E é chamado de JWT (JSON Web Tokens).
JSON Web Token (JWT) é um padrão aberto (RFC 7519) que define uma maneira compacta e independente para transmitir informações com segurança entre as partes como um objeto JSON. Essas informações podem ser verificadas e confiáveis porque estão assinadas digitalmente. Os JWTs podem ser assinados usando um segredo (com o algoritmo HMAC) ou um par de chaves pública / privada usando RSA.
Eu recomendo fortemente olhar para JWTs. Eles são uma solução muito mais simples para o problema quando comparados com soluções alternativas.
https://jwt.io/introduction/
fonte
Você pode criar Sessão no servidor e compartilhar
sessionId
entre cliente e servidor com cada chamada REST.Primeiro pedido RESTO autenticar:
/authenticate
. Retorna a resposta (de acordo com o formato do cliente) comsessionId: ABCDXXXXXXXXXXXXXX
;Armazenar esta
sessionId
emMap
com sessão real.Map.put(sessionid, session)
ou usarSessionListener
para criar e destruir chaves para você;Obtenha o sessionid com cada chamada REST, como
URL?jsessionid=ABCDXXXXXXXXXXXXXX
(ou outra forma);HttpSession
do mapa usandosessionId
;fonte
Eu usaria se um aplicativo redirecionasse um usuário para o seu site com um parâmetro de id do aplicativo, uma vez que o usuário aprova a solicitação, gere um token exclusivo que é usado pelo outro aplicativo para autenticação. Desta forma, os outros aplicativos não estão lidando com as credenciais do usuário e outros aplicativos podem ser adicionados, removidos e gerenciados pelos usuários. O Foursquare e alguns outros sites autenticam dessa forma e é muito fácil de implementar como o outro aplicativo.
fonte
Além da autenticação, sugiro que você pense no quadro geral. Considere tornar seu serviço RESTful de back-end sem qualquer autenticação; em seguida, coloque algum serviço de camada intermediária necessária de autenticação muito simples entre o usuário final e o serviço de backend.
fonte