As sessões realmente violam o RESTfulness?

491

O uso de sessões em uma API RESTful realmente viola o RESTfulness? Tenho visto muitas opiniões indo em qualquer direção, mas não estou convencido de que as sessões sejam RESTless . Do meu ponto de vista:

  • a autenticação não é proibida para RESTfulness (caso contrário, haveria pouco uso nos serviços RESTful)
  • a autenticação é feita enviando um token de autenticação na solicitação, geralmente o cabeçalho
  • esse token de autenticação precisa ser obtido de alguma forma e pode ser revogado; nesse caso, ele precisa ser renovado
  • o token de autenticação precisa ser validado pelo servidor (caso contrário, não seria autenticação)

Então, como as sessões violam isso?

  • do lado do cliente, as sessões são realizadas usando cookies
  • cookies são simplesmente um cabeçalho HTTP extra
  • um cookie de sessão pode ser obtido e revogado a qualquer momento
  • os cookies de sessão podem ter uma vida útil infinita, se necessário
  • o ID da sessão (token de autenticação) é validado no servidor

Como tal, para o cliente, um cookie de sessão é exatamente o mesmo que qualquer outro mecanismo de autenticação baseado em cabeçalho HTTP, exceto pelo uso do Cookiecabeçalho em vez do Authorizationcabeçalho proprietário ou de algum outro cabeçalho proprietário. Se não havia nenhuma sessão anexada ao valor do cookie do lado do servidor, por que isso faria diferença? A implementação do lado do servidor não precisa preocupar o cliente, desde que o servidor se comporte com RESTful. Como tal, os cookies por si só não devem tornar uma API RESTless , e as sessões são simplesmente cookies para o cliente.

Minhas suposições estão erradas? O que torna os cookies de sessão RESTless ?

deceze
fonte
5
Eu cobri esse problema exato aqui: stackoverflow.com/questions/1296421/rest-complex-applications/…
Will Hartung
5
Para adicionar isso, se você estiver usando apenas a sessão para autenticação, por que não usar os cabeçalhos fornecidos? Caso contrário, e você está usando a sessão para outro estado da conversa, isso está violando a restrição Stateless do REST.
Will Hartung
2
@ Will Obrigado. Parece que você está falando sobre sessões para armazenar temporariamente dados enviados pelo usuário, enquanto no meu caso eu estou falando apenas deles como um detalhe de implementação para autenticação. Pode ser de onde vem a discordância?
deceze
3
@ deceze Meu único argumento é que, se você usar um cabeçalho para representar um token de autenticação, o HTTP fornecerá um além de um cookie genérico. Portanto, por que não usar isso e manter a semântica gratuita que você obtém com ele (quem vê a carga pode ver que há um token de autenticação atribuído a ela).
Will Hartung
7
Claro, mas por que não criar seus próprios cabeçalhos ou seqüestrar outro cabeçalho para o token de autenticação? Use o cabeçalho X-XYZZY. É apenas sintaxe, certo? Os cabeçalhos transmitem informações. O cabeçalho de autorização é mais "auto-documentável" do que o seu cookie, porque "todos" sabem para que serve o cabeçalho de autenticação. Se eles apenas veem JSESSIONID (ou o que seja), eles não podem fazer nenhuma suposição, ou pior, fazer suposições erradas (o que mais ele está armazenando na sessão, para que mais isso é usado etc.). Você nomeou suas variáveis ​​no seu código Aq12hsg? Não, claro que não. A mesma coisa se aplica aqui.
Will Hartung

Respostas:

299

Primeiro, vamos definir alguns termos:

  • Repousante:

    Pode-se caracterizar aplicativos em conformidade com as restrições REST descritas nesta seção como "RESTful". [15] Se um serviço violar alguma das restrições necessárias, não poderá ser considerado RESTful.

    de acordo com a wikipedia .

  • restrição apátrida:

    Em seguida, adicionamos uma restrição à interação cliente-servidor: a comunicação deve ter natureza sem estado, como no estilo cliente-sem estado-servidor (CSS) da Seção 3.4.3 (Figura 5-3), de modo que cada solicitação do cliente para O servidor deve conter todas as informações necessárias para entender a solicitação e não pode tirar proveito de nenhum contexto armazenado no servidor. O estado da sessão é, portanto, mantido inteiramente no cliente.

    de acordo com a dissertação de Fielding .

Portanto, as sessões do servidor violam a restrição sem estado do REST e também o RESTfulness.

Assim, para o cliente, um cookie de sessão é exatamente o mesmo que qualquer outro mecanismo de autenticação baseado em cabeçalho HTTP, exceto que ele usa o cabeçalho Cookie em vez da Autorização ou algum outro cabeçalho proprietário.

Por cookies de sessão, você armazena o estado do cliente no servidor e, portanto, sua solicitação tem um contexto. Vamos tentar adicionar um balanceador de carga e outra instância de serviço ao seu sistema. Nesse caso, você precisa compartilhar as sessões entre as instâncias de serviço. É difícil manter e estender esse sistema, por isso é muito ruim ...

Na minha opinião, não há nada errado com os cookies. A tecnologia de cookies é um mecanismo de armazenamento do lado do cliente, no qual os dados armazenados são anexados automaticamente aos cabeçalhos de cookies a cada solicitação. Não conheço uma restrição REST que tenha problemas com esse tipo de tecnologia. Portanto, não há problema com a própria tecnologia, o problema é com seu uso. Fielding escreveu uma subseção sobre por que ele acha que os cookies HTTP são ruins.

Do meu ponto de vista:

  • a autenticação não é proibida para RESTfulness (caso contrário, haveria pouco uso nos serviços RESTful)
  • a autenticação é feita enviando um token de autenticação na solicitação, geralmente o cabeçalho
  • esse token de autenticação precisa ser obtido de alguma forma e pode ser revogado; nesse caso, ele precisa ser renovado
  • o token de autenticação precisa ser validado pelo servidor (caso contrário, não seria autenticação)

Seu ponto de vista foi bastante sólido. O único problema foi com o conceito de criação de token de autenticação no servidor. Você não precisa dessa parte. O que você precisa é armazenar nome de usuário e senha no cliente e enviá-lo com todas as solicitações. Você não precisa fazer mais do que autenticação básica HTTP e uma conexão criptografada:

Figura 1. - Autenticação sem estado por clientes confiáveis

  • Figura 1. - Autenticação sem estado por clientes confiáveis

Você provavelmente precisará de um cache de autenticação na memória no servidor para agilizar as coisas, pois é necessário autenticar todas as solicitações.

Agora, isso funciona muito bem por clientes confiáveis ​​escritos por você, mas e quanto a clientes de terceiros? Eles não podem ter o nome de usuário e senha e todas as permissões dos usuários. Portanto, você deve armazenar separadamente quais permissões um cliente de terceiros pode ter por um usuário específico. Assim, os desenvolvedores do cliente podem registrar clientes de terceiros e obter uma chave de API exclusiva e os usuários podem permitir que clientes de terceiros acessem parte de suas permissões. Como ler o nome e endereço de e-mail ou listar seus amigos, etc ... Depois de permitir um cliente de terceiros, o servidor gerará um token de acesso. Esses tokens de acesso podem ser usados ​​pelo cliente de terceiros para acessar as permissões concedidas pelo usuário, como:

Figura 2. - Autenticação sem estado por clientes de terceiros

  • Figura 2. - Autenticação sem estado por clientes de terceiros

Portanto, o cliente de terceiros pode obter o token de acesso de um cliente confiável (ou diretamente do usuário). Depois disso, ele pode enviar uma solicitação válida com a chave da API e o token de acesso. Esse é o mecanismo de autenticação de terceiros mais básico. Você pode ler mais sobre os detalhes da implementação na documentação de todos os sistemas de autenticação de terceiros, por exemplo, OAuth. É claro que isso pode ser mais complexo e mais seguro, por exemplo, você pode assinar os detalhes de cada solicitação no lado do servidor e enviar a assinatura junto com a solicitação, e assim por diante ... A solução real depende da necessidade do seu aplicativo.

inf3rno
fonte
5
Sim, você está completamente certo. Desde que eu postei essa pergunta, eu vim totalmente para ver isso. Os cookies de sessão não são nada de especial quando analisados ​​nos detalhes técnicos, mas falta a floresta para as árvores. Aceitou sua resposta por causa dos bons gráficos. ;)
deceze
1
Ok, repensei, a resposta do serviço REST não deve depender da autorização, por isso acho que as 2 primeiras soluções estão 100% corretas e as demais estão corretas se o serviço usar as informações apenas para decidir se permite a solicitação ou não. Então, acho que as permissões do usuário devem afetar a representação do recurso atual.
Inf3rno
1
Vou criar uma pergunta sobre a dependência de permissões das representações. Estenderei essa resposta assim que tiver a solução adequada.
Inf3rno 01/12/2018
3
@ inf3rno, é verdade que um serviço totalmente RESTful não pode depender de cookies de sessão para autenticação da maneira que é tradicionalmente implementado. No entanto, você pode usar cookies para executar a autenticação se o cookie contiver todas as informações de estado que o servidor precisará posteriormente. Você também pode proteger o cookie contra violações, assinando-o com um par de chaves pública / privada. Veja meus comentários abaixo.
precisa saber é o seguinte
3
Não entendo por que todo mundo parece aceitar o comentário, você deve armazenar senhas no lado do cliente e enviá-las a cada solicitação. Essa é uma prática muito ruim e coloca em risco os dados confidenciais de seus clientes. Uma senha com unhashed (que teria que ser para enviá-la repetidamente) nunca deve ser armazenada em qualquer lugar. Se aceitarmos isso, você estará usando tokens como a maioria dos sistemas de autenticação. Nesse caso, qualquer mecanismo usado para dimensionar o repositório de tokens terá preocupações de escalabilidade iguais a qualquer escalabilidade de sessão.
Lvoelk # 19/18
334

Primeiro de tudo, o REST não é uma religião e não deve ser abordado como tal. Embora existam vantagens nos serviços RESTful, você deve seguir apenas os princípios do REST, na medida em que façam sentido para o seu aplicativo.

Dito isto, autenticação e estado do lado do cliente não violam os princípios REST. Embora o REST exija que as transições de estado sejam sem estado, isso se refere ao próprio servidor. No coração, todo o REST é sobre documentos. A idéia por trás da apatridia é que o SERVIDOR é apátrida, não os clientes. Qualquer cliente que emita uma solicitação idêntica (mesmos cabeçalhos, cookies, URI, etc.) deve ser levado para o mesmo local no aplicativo. Se o site armazenasse o local atual do usuário e a navegação gerenciada atualizando essa variável de navegação no servidor, o REST seria violado. Outro cliente com informações de solicitação idênticas seria levado para um local diferente, dependendo do estado do servidor.

Os serviços web do Google são um exemplo fantástico de um sistema RESTful. Eles exigem que um cabeçalho de autenticação com a chave de autenticação do usuário seja passado a cada solicitação. Isso viola ligeiramente os princípios REST, porque o servidor está rastreando o estado da chave de autenticação. O estado dessa chave deve ser mantido e possui algum tipo de data / hora de vencimento após o qual não concede mais acesso. No entanto, como mencionei na parte superior da minha postagem, sacrifícios devem ser feitos para permitir que um aplicativo realmente funcione. Dito isso, os tokens de autenticação devem ser armazenados de forma a permitir que todos os clientes possíveis continuem concedendo acesso durante seus períodos válidos. Se um servidor estiver gerenciando o estado da chave de autenticação até o ponto em que outro servidor com balanceamento de carga não puder assumir as solicitações com base nessa chave, você começou realmente a violar os princípios do REST. Os serviços do Google garantem que, a qualquer momento, você possa pegar um token de autenticação que estava usando no telefone no servidor de equilíbrio de carga A e acessar o servidor de equilíbrio de carga B na área de trabalho e ainda ter acesso ao sistema e ser direcionado aos mesmos recursos se os pedidos eram idênticos.

O que tudo se resume é que você precisa garantir que seus tokens de autenticação sejam validados em um armazenamento de backup de algum tipo (banco de dados, cache, o que for) para garantir a preservação do maior número possível de propriedades REST.

Espero que tudo isso faça sentido. Você também deve verificar a seção Restrições do artigo da wikipedia sobre Representational State Transfer, se ainda não o tiver feito. É particularmente esclarecedor sobre o que os princípios do REST estão realmente argumentando e por quê.

Jared Harding
fonte
6
Eu reformularia sua declaração inicial. Use REST apenas se as restrições do REST fizerem sentido do seu aplicativo. Você é livre para aplicar um subconjunto dessas restrições e obterá um subconjunto dos benefícios. No entanto, nesse ponto, você criou seu próprio estilo arquitetural. Isso não é algo ruim, na verdade, é sobre isso que os quatro primeiros capítulos da dissertação de Roy tratam, design de princípios. O REST foi apenas um exemplo.
Darrel Miller 20/05
4
@ Darrel Um ponto bastante justo. Sinceramente, não sei como o Google faz isso, mas o tempo de expiração pode ser codificado no token de autenticação. Eu acredito que meu ponto maior ainda permanece. Existem alguns tipos de estado que simplesmente devem ser mantidos e, desde que você entenda por que o REST exige apatridia, você poderá violá-lo de uma maneira que faça sentido sem muitas repercussões no restante do sistema e as vantagens de uma arquitetura RESTful .
Jared Harding
7
Como nenhum outro argumento foi apresentado até agora, estou aceitando essa resposta bem escrita. Penso que a parte importante é que servidor sem estado não significa servidor sem estado , algo que penso ser frequentemente mal interpretado ou mal aplicado. O servidor pode (e geralmente deve ) ter qualquer estado que desejar, desde que se comporte de forma independente .
deceze
10
Eu ouvi tantas pregações que as sessões não são tranqüilas. A autenticação básica HTTP é um retrocesso real, se você estiver tentando criar um aplicativo Web.
Ben Thurley
1
@Micah Henning, você está assumindo que o servidor requer informações de estado para validar o token de autenticação. Podemos razoavelmente supor que você não pode forjar um token que foi assinado por um par de chaves pública / privada, se você não conhece a chave privada. Para verificar se o token é válido, tudo o que você precisa é a chave pública. Eu ainda mantenho que a autenticação completamente RESTful é possível.
Jcoffland # 03/14
12

Os cookies não são para autenticação. Por que reinventar uma roda? O HTTP possui mecanismos de autenticação bem projetados. Se usarmos cookies, passamos a usar o HTTP apenas como protocolo de transporte; portanto, precisamos criar nosso próprio sistema de sinalização, por exemplo, para informar aos usuários que eles forneceram autenticação incorreta (o HTTP 401 estaria incorreto, como provavelmente não fornecer Www-Authenticatea um cliente, pois as especificações HTTP requerem :)). Note-se também que Set-Cookieé apenas uma recomendação para o cliente. Seu conteúdo pode ou não ser salvo (por exemplo, se os cookies estiverem desabilitados), enquanto o Authorizationcabeçalho é enviado automaticamente a cada solicitação.

Outro ponto é que, para obter um cookie de autorização, você provavelmente desejará fornecer suas credenciais em algum lugar primeiro? Se sim, não seria RESTless? Exemplo simples:

  • Você tenta GET /asem cookie
  • Você recebe uma solicitação de autorização de alguma forma
  • Você vai e autoriza de alguma forma como POST /auth
  • Você recebe Set-Cookie
  • Você tenta GET /a com cookie. Mas se GET /acomporta idempotentemente neste caso?

Para resumir, acredito que, se acessarmos algum recurso e precisarmos autenticar, devemos autenticar nesse mesmo recurso , e não em nenhum outro lugar.

starteleport
fonte
1
Enquanto isso, cheguei mais a esse ponto de vista também. Eu acho que tecnicamente faz pouca diferença, são apenas cabeçalhos HTTP. É verdade, porém, que o comportamento da autenticação em si não é RESTful, se for necessário fazer login através de um endereço separado. Portanto, os cookies são apenas um sintoma de um problema maior no sistema de autenticação.
deceze
Isso realmente não explica o fato de que os navegadores da Web suportam apenas Authorization: Basicou Digest. Se você deseja fazer algo mais avançado do que autenticação básica ou resumida (e deve) em um contexto de navegador, precisará de algo diferente do Authorizationcabeçalho.
26716 Oliver Oliverworth
1
Absolutamente - se você estiver executando JS puro, tudo ficará bem (exceto, por exemplo, Websockets). Mas o que quero dizer é que a autenticação baseada em API não é necessariamente a única consideração em um cenário de navegador.
26616 Oliver Charlesworth
5
GET /asem cookie e com cookie são claramente duas solicitações diferentes e é aceitável que elas se comportem de maneira diferente.
TRiG 31/07/19
1
Para adicionar ao @TRiG, ​​seguindo essa lógica, o GET /acabeçalho de autenticação também é o mesmo que GET /ao cabeçalho de autenticação, tornando-o igualmente inutilizável para o REST. Se você vai tratar um cabeçalho http de maneira diferente de outro, abordará isso no mínimo.
Jasper
7

Na verdade, RESTfulness se aplica apenas a RECURSOS, conforme indicado por um Universal Resource Identifier. Portanto, falar sobre coisas como cabeçalhos, cookies etc. em relação ao REST não é realmente apropriado. O REST pode funcionar com qualquer protocolo, mesmo que isso seja feito rotineiramente por HTTP.

O principal determinante é o seguinte: se você enviar uma chamada REST, que é um URI, depois que a chamada chegar com êxito ao servidor, esse URI retornará o mesmo conteúdo, assumindo que nenhuma transição tenha sido executada (PUT, POST, DELETE) ? Esse teste excluiria o retorno de erros ou solicitações de autenticação, pois, nesse caso, a solicitação ainda não foi enviada ao servidor, ou seja, o servlet ou aplicativo que retornará o documento correspondente ao URI fornecido.

Da mesma forma, no caso de um POST ou PUT, você pode enviar um determinado URI / carga útil e, independentemente de quantas vezes você envia a mensagem, ela sempre atualiza os mesmos dados, para que os GETs subsequentes retornem um resultado consistente?

O REST é sobre os dados do aplicativo, não sobre as informações de baixo nível necessárias para transferir esses dados.

Na seguinte postagem no blog, Roy Fielding deu um bom resumo de toda a ideia REST:

http://groups.yahoo.com/neo/groups/rest-discuss/conversations/topics/5841

"Um sistema RESTful progride de um estado estacionário para o próximo, e cada um desses estados estáveis ​​é um estado inicial potencial e um estado final potencial. Ou seja, um sistema RESTful é um número desconhecido de componentes que obedecem a um conjunto simples de regras de modo que elas sempre estejam no REST ou em transição de um estado RESTful para outro estado RESTful Cada estado pode ser completamente compreendido pela (s) representação (ões) que ele contém e pelo conjunto de transições que ele fornece, com as transições limitadas a um uniforme conjunto de ações para ser compreensível.O sistema pode ser um diagrama de estados complexo, mas cada agente do usuário só pode ver um estado de cada vez (o estado estacionário atual) e, portanto, cada estado é simples e pode ser analisado independentemente. usuário, OTOH, pode criar suas próprias transições a qualquer momento (por exemplo, digite um URL, selecione um marcador,abra um editor, etc.) ".


Indo para a questão da autenticação, seja ela realizada por meio de cookies ou cabeçalhos, desde que as informações não façam parte da carga útil do URI e POST, ela realmente não tem nada a ver com o REST. Portanto, em relação ao estado sem estado, estamos falando apenas dos dados do aplicativo.

Por exemplo, quando o usuário digita dados em uma tela da GUI, o cliente mantém o controle de quais campos foram inseridos, quais não, campos obrigatórios ausentes etc. Isso é tudo CONTEXTO DO CLIENTE e não deve ser enviado ou rastreado pelo servidor. O que é enviado ao servidor é o conjunto completo de campos que precisam ser modificados no recurso IDENTIFIED (pelo URI), de modo que ocorra uma transição nesse recurso de um estado RESTful para outro.

Portanto, o cliente acompanha o que o usuário está fazendo e envia apenas transições de estado logicamente completas para o servidor.

Ken Kopelson
fonte
3
Não vejo como isso lança alguma luz sobre a questão colocada.
Jcoffland # 03/02
1

A transação HTTP, autenticação de acesso básico, não é adequada para o RBAC, porque a autenticação de acesso básico usa sempre o nome de usuário criptografado: password para identificar, enquanto o que é necessário no RBAC é a função que o usuário deseja usar para uma chamada específica. O RBAC não valida permissões no nome de usuário, mas nas funções.

Você pode tentar concatenar assim: usernameRole: password, mas isso é uma prática ruim e também é ineficiente porque, quando um usuário tem mais funções, o mecanismo de autenticação precisaria testar todas as funções na concatenação e todas as chamadas novamente. Isso destruiria uma das maiores vantagens técnicas do RBAC, a saber, um teste de autorização muito rápido.

Portanto, esse problema não pode ser resolvido usando a autenticação de acesso básica.

Para resolver esse problema, a manutenção da sessão é necessária e isso parece, de acordo com algumas respostas, em contradição com o REST.

É disso que eu gosto na resposta de que o REST não deve ser tratado como religião. Em casos comerciais complexos, na área da saúde, por exemplo, o RBAC é absolutamente comum e necessário. E seria uma pena se eles não pudessem usar o REST porque todos os designers de ferramentas REST tratariam o REST como uma religião.

Para mim, não há muitas maneiras de manter uma sessão por HTTP. Pode-se usar cookies, com um sessionId, ou um cabeçalho com um sessionId.

Se alguém tiver outra idéia, ficarei feliz em ouvi-la.

Bert Verhees
fonte
-4
  1. As sessões não são RESTless
  2. Você quer dizer que o serviço REST é apenas para uso http ou eu entendi errado? A sessão baseada em cookie deve ser usada apenas para serviços baseados em http (!) Próprios! (Pode ser um problema trabalhar com cookies, por exemplo, do Mobile / Console / Desktop / etc.)
  3. se você fornecer um serviço RESTful para desenvolvedores de festas em 3D, nunca use uma sessão baseada em cookie, use tokens para evitar problemas de segurança.
Máxima
fonte
3
o cookie não deve ser usado para armazenar uma chave de sessão para uma sessão no servidor que contém o token de autenticação. mas se o cookie possuir o próprio token de autenticação, é uma solução viável. (é claro que o cookie deve ser httponly e segura)
roberkules