ti-enxame.com

Corrija o código de status do http para o recurso que requer autorização

Parece haver muita confusão sobre o código de status http correto para retornar se o usuário tentar acessar uma página que requer que o usuário faça o login.

Então, basicamente, qual código de status será enviado quando eu mostrar a página de login?

Tenho certeza de que precisamos usar um código de status no intervalo 4xx.

Eu não estou falando sobre autenticação HTTP aqui, então é pelo menos 1 código de status que não vamos usar (401 Unauthorized).

Agora, o que devemos usar? As respostas (também aqui no SO) parecem variar:

De acordo com a resposta aqui devemos usar 403 Forbidden.

Mas na descrição do código de status é:

A autorização não ajudará e a solicitação NÃO DEVE ser repetida.

Bem, isso não parece o caminho certo. Desde que a autorização ajudaria.

Então vamos verificar outra resposta. A resposta aqui mesmo não usa o intervalo 4xx, mas usa 302 Found

A descrição do código de status 302 Found:

O recurso solicitado reside temporariamente em um URI diferente. Como o redirecionamento pode ser alterado ocasionalmente, o cliente deve continuar a usar o URI de solicitação para solicitações futuras. Essa resposta é apenas armazenável em cache se indicada por um campo de cabeçalho Cache-Control ou Expires.

Eu acho que também não é o que eu quero. Como não é o recurso solicitado que reside em um URI diferente. Mas sim um recurso completamente diferente (página de login vs página de conteúdo autenticado).

Então eu segui em frente e escolhi outro resposta surpreendentemente com outra solução.

Esta resposta sugere que escolhamos 400 Bad Request.

A descrição deste código de status é:

A solicitação não pôde ser entendida pelo servidor devido à sintaxe malformada. O cliente não deve repetir o pedido sem modificações.

Eu acho que o servidor entendeu bem o pedido, mas apenas se recusa a dar acesso antes que o usuário seja autenticado.

Outro resposta também diz que uma resposta 403 está correta, no entanto, termina com:

Se esse for um site voltado para o público em que você está tentando negar acesso com base em um cookie de sessão [é o que eu faço], 200 com um corpo apropriado para indicar que o login é necessário ou um redirecionamento temporário para uma página de login é frequentemente melhor.

Então 403 está correto, mas 200 ou 302 é o melhor.

Ei! É o que estou procurando: a melhor solução. Mas o melhor não deveria ser o mesmo que o correto? E por que seria o melhor?

Obrigado a todos que chegaram até aqui nesta questão :)

Eu sei que não deveria me preocupar muito com isso. E eu acho que essa questão é mais hipotética (não realmente, mas usada por causa da falta de uma palavra melhor).

Mas esta questão está me assombrando por algum tempo agora.

E se eu tivesse sido um gerente (que acabou de pegar algumas palavras que soam legais como sempre fazem), eu teria dito: mas, mas, mas, mas a tranquilidade é importante. :-)

Então: o que é the right way™ de usar um código de status na situação acima (se houver)?

tl; dr

Qual é a resposta correta do código de status http quando um usuário tenta acessar uma página que requer login?

56
PeeHaa

Se o usuário não tiver fornecido nenhuma credencial e sua API exigir isso, retorne um 401 - Unauthorized. Isso vai desafiar o cliente a fazê-lo. Geralmente, há pouco debate sobre esse cenário específico.

Se o usuário forneceu credenciais válidas, mas elas são insuficientes para acessar o recurso solicitado (talvez as credenciais tenham sido para uma conta freemium, mas o recurso solicitado é apenas para seus usuários pagos), você tem algumas opções dadas a frouxidão de algumas das definições de código HTTP:

  1. Retornar 403 - Forbidden. Isso é mais descritivo e é normalmente entendido como "as credenciais fornecidas eram válidas, mas ainda não eram suficientes para conceder acesso"
  2. Retornar 401 - Unauthorized. Se você é paranóico com relação à segurança, talvez não queira devolver as informações extras ao cliente como foi retornado em (1) acima
  3. Retornar 401 ou 403, mas com informações úteis no corpo da resposta, descrevendo os motivos pelos quais o acesso está sendo negado. Mais uma vez, essa informação pode ser mais do que você gostaria de fornecer no caso de ajudar um pouco os atacantes.

Pessoalmente, sempre usei o número 1 para o cenário em que as credenciais válidas foram aprovadas, mas a conta à qual estão associadas não tem acesso ao recurso solicitado.

43
Brian Kelly

Você pede "o melhor", "o caminho certo" e "o correto", por sua vez, o que torna difícil responder a essa pergunta porque esses critérios não são necessariamente intercambiáveis ​​e podem, de fato, conflitar - especialmente quando se trata de RESTfulness. .

A "melhor" resposta depende da sua aplicação. Você está construindo um aplicativo da Web baseado em navegador (POBB) Plain Old? Você está construindo um cliente nativo (ex. IOS ou Android) e acessando um serviço pela Web? Você está fazendo uso pesado de AJAX para gerar atualizações de páginas da web? É enrolar o cliente pretendido?

Vamos supor que você esteja criando um aplicativo da Web tradicional. Vamos ver como o Google faz isso (saída cortada por brevidade):

$ curl -v http://gmail.com/
< HTTP/1.1 301 Moved Permanently
< Location: http://mail.google.com/mail/
< Content-Type: text/html; charset=UTF-8
< Content-Length: 225
< ...

Primeiro, o Google nos redireciona para o URL "verdadeiro" do Gmail (usando um redirecionamento 302).

$ curl -v http://mail.google.com/mail/
< HTTP/1.1 302 Moved Temporarily
< Location: https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue=http://mail.google.com/mail/&scc=1&ltmpl=default&ltmplcache=2
< Content-Type: text/html; charset=UTF-8
< Content-Length: 352
< ...

E então ele nos redireciona para a página de login (usando um redirecionamento 302).

$ curl -v 'https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue=http://mail.google.com/mail/&scc=1&ltmpl=default&ltmplcache=2'
< HTTP/1.1 200 OK
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< ...

A própria página de login é entregue com o código de status 200 !

Por que isso?

Do ponto de vista da experiência do usuário, se um usuário acessa uma página que não pode visualizar porque não está autenticado, você deseja levar o usuário a uma página que permita corrigi-lo (por meio de login). Neste exemplo, a página de login é autônoma e é apenas outra página (é por isso que 200 é apropriado).

Você pode lançar uma página 4XX com uma explicação e um link para a página de login. Isso pode, na verdade, parecer mais RESTful. Mas é uma experiência pior do usuário.

Ok, mas existe um caso em que algo como 403 faz sentido? Absolutamente.

Primeiro, note que 403 não está bem definido na especificação. Para entender como deve ser usado, você precisa ver como isso é implementado no campo.

O 403 é comumente usado por servidores da Web como Apache e IIS como o código de status de páginas retornadas quando o navegador solicita uma listagem de diretórios (um URI que termina em "/"), mas o servidor tem listas de diretórios desabilitadas. Neste caso, 403 é realmente um 404 especializado, e não há muito que você possa fazer para o usuário, exceto que ele saiba o que deu errado.

No entanto, aqui está um exemplo de um site que usa o 403 para sinalizar para o usuário que ele/ela não tem privilégios suficientes e qual ação tomar para corrigir a situação (confira o completo resposta para detalhes):

curl -v http://www.w3.org/Protocols/rfc2616/
< HTTP/1.1 403 Forbidden
< Content-Type: text/html; charset=iso-8859-1
< Content-Length: 1564
< ...

(Como um aparte, 403 também é visto em APIs baseadas na web, como a API do Twitter; aqui, 403 significa "A solicitação é entendida, mas foi recusada. Uma mensagem de erro explicará o porquê. Esse código é usado quando as solicitações são sendo negado devido a limites de atualização. ")

Como uma melhoria, vamos supor, no entanto, que você não queira redirecionar o usuário para uma página de login, ou forçar o usuário a seguir um link para a página de login. Em vez disso, você deseja exibir o formulário de login na página que o usuário é impedido de ver. Se eles forem autenticados com sucesso, eles verão o conteúdo quando a página for recarregada; se eles falharem, eles obtêm o formulário de login novamente. Eles nunca navegam para outro URL.

Nesse caso, um código de status 403 faz muito sentido e é homólogo ao caso 401, com a ressalva de que o navegador não exibirá uma caixa de diálogo solicitando que o usuário autentique - o formulário está na própria página .

Essa abordagem para autenticação não é comum, mas poderia fazer sentido, e é preferível para IMHO para as soluções pop-up-a-javascript-modal-para-log-in que os desenvolvedores tentam implementar.

Tudo se resume à pergunta, você quer redirecionar ou não?

Adicional: pensamentos sobre o código de status 401 ...

O código de status 401 - e a autenticação básica/digest associada - tem muitas coisas a seu favor. Ele é adotado pela especificação HTTP, é suportado por todos os principais navegadores, não é inerentemente não-RESTful ... O problema é que, do ponto de vista da experiência do usuário, é muito pouco atraente. Há o diálogo pop-up crachável, não estilizado, falta de uma solução elegante para sair, etc. Se você (ou seus stakeholders/clientes) pode viver com esses problemas (um grande se) então ele pode se qualificar como o "correto "solução.

26
toddsundsted

Acordado. REST é apenas um estilo, não um protocolo estrito. Muitos serviços da Web públicos se desviam desse estilo. Você pode construir seu serviço para devolver o que quiser. Apenas certifique-se de que seus clientes saibam como os códigos de retorno devem ser esperados.

Pessoalmente, eu sempre usei 401 (não autorizado) para indicar que um usuário não autenticado solicitou um recurso que requer um login. Eu então solicito que o aplicativo cliente guie o usuário para o login.

Eu uso 400 (solicitação incorreta) em resposta a uma tentativa de logon com credenciais inválidas.

O HTTP 302 (movido) parece mais apropriado para aplicativos da web em que o cliente é um navegador. Os navegadores geralmente seguem o endereço redirecionado na resposta. Isso pode ser útil para guiar o usuário para uma página de logon.

10
EJK

Eu não estou falando sobre autenticação HTTP aqui, então é pelo menos um código de status que não vamos usar (401 não autorizado).

Errado. 401 faz parte do protocolo de transferência de hipertexto (RFC 2616 Fielding, et al.), Mas não se limita à autenticação HTTP. Além disso, é o único código de status indicando que a solicitação requer autenticação do usuário.

302 e 200 códigos poderia ser usado e é mais fácil de implementar em alguns cenários, mas não todos. E se você quiser obedecer às especificações, 401 é a única resposta correta que existe.

E 403 é de fato o código mais errado para retornar. Como você afirmou corretamente ...

A autorização não ajudará e a solicitação NÃO DEVE ser repetida.

Então, isso claramente não é adequado para indicar que a autorização é uma opção.

Eu manteria o padrão: 401 Não autorizado

-

UPDATE

Para adicionar um pouco mais de informação, levantando a confusão relacionada com ...

A resposta DEVE incluir um campo de cabeçalho WWW-Authenticate (seção 14.47) contendo um desafio aplicável ao recurso solicitado.

Se você acha que isso vai impedir você de usar um 401, você precisa se lembrar de que há mais:

"O valor do campo consiste em pelo menos um desafio que indica o (s) esquema (s) de autenticação e parâmetros aplicáveis ​​ao Request-URI." 

Este "indicando o esquema de autenticação" significa que você pode optar por outros esquemas de autenticação! 

O protocolo HTTP (RFC 2616) define uma estrutura simples para esquemas de autenticação de acesso, mas você NÃO TEM que usar a estrutura THAT. 

Em outras palavras: você não está preso ao habitual WWW-Auth. Você só precisa indicar COMO seu webapp faz sua autorização e oferecer os dados correspondentes no cabeçalho, isso é tudo. De acordo com as especificações, usando um 401, você pode escolher seu próprio veneno de autorização! E é aí que o seu "webapp" pode fazer o que você quer fazer quando se trata do cabeçalho 401 e da sua implementação de autorização. 

Não deixe as especificações confundirem você, achando que você tem que usar o esquema usual de autenticação HTTP. Você não! A única coisa que as especificações realmente impõem: basta você/DEVE identificar o esquema de autenticação do seu webapp e passar parâmetros relacionados para permitir que o solicitante inicie possíveis tentativas de autorização. 

E se você ainda não tiver certeza, posso colocar tudo isso em uma perspectiva simples, mas compreensível: digamos que você vá inventar um novo esquema de autorização amanhã, então as especificações permitem que você use isso também. Se as especificações tivessem uma implementação restrita de implementações de tecnologia de autorização mais recentes, essas especificações teriam sido modificadas há muito tempo. As especificações definem padrões, mas não limitam realmente a multiplicidade de implementações potenciais.

8
user1093284

Seu "TL; DR" não corresponde à versão "TL".

A resposta apropriada para solicitar um recurso que você precisa de autorização para solicitar é 401.

302 não é a resposta adequada, porque, de fato, o recurso não está disponível em outro lugar. O URL original estava correto, o cliente simplesmente não tinha os direitos. Se você seguir o redirecionamento, você não conseguirá realmente o que está procurando. Você cai em algum fluxo de trabalho ad hoc que não tem nada a ver com o recurso.

403 está incorreto. 403 é o erro "não consigo chegar lá daqui". Você simplesmente não pode ver isso, eu não me importo com quem você é. Alguns argumentam que 403 e 404 são semelhantes. A diferença é simplesmente com 403, o servidor está dizendo "sim, eu tenho, mas você não pode", enquanto 404 diz "Eu não sei nada sobre o que você está falando." Os especialistas em segurança argumentariam que 404 é "mais seguro". Por que dizer a eles algo que não precisam saber.

O problema que você está encontrando não tem nada a ver com REST ou HTTP. Seu problema é tentar configurar um relacionamento stateful entre o cliente e o servidor, manifestado no final por meio de algum cookie. Todo o recurso -> 302 -> página de login é toda sobre a experiência do usuário usando o hack que é conhecido como o navegador da Web, que acontece de ser ambos, em forma de estoque, um cliente HTTP ruim e um péssimo REST participante . 

HTTP tem um mecanismo de autorização. O cabeçalho de autorização. A experiência do usuário em torno dele, em um navegador genérico, é horrível. Então ninguém usa isso. 

Portanto, não há resposta HTTP adequada (bem, existe, 401, mas não/não posso usar isso). Não há uma resposta REST adequada, já que REST normalmente depende do protocolo subjacente (HTTP neste caso, mas já abordamos isso).

Assim. 302 -> 200 para a página de login é tudo o que ela escreveu. Isso é o que você ganha. Se você não estivesse usando o navegador, ou fizesse tudo via XHR ou algum outro cliente personalizado, isso não seria um problema. Você apenas usaria o cabeçalho de Autorização, seguiria o protocolo HTTP e aproveitaria um esquema como o DIGEST ou o que a AWS usa e pronto. Então você pode usar os padrões apropriados para responder perguntas como essas.

3
Will Hartung

Como você apontou, 403 Forbidden é explicitamente definido com a frase " A autorização não ajudará ", mas vale a pena notar que os autores quase certamente estavam se referindo aqui à autorização HTTP (que certamente não ajudará como seu site utiliza um esquema de autorização diferente). De fato, dado que o código de status é um sinal para o user agent em vez de user , tal código estaria correto na medida em que qualquer autorização que o agente tentasse fornecer não ajudaria além disso, com o processo de autorização necessário (cf 401 Unauthorized).

No entanto, se você tomar essa definição de 403 Forbidden literalmente e achar que ela ainda é inadequada, talvez 409 Conflict possa ser aplicado? Conforme definido em RFC 2616 §10.4.10 :

 A solicitação não pôde ser concluída devido a um conflito com o estado atual 
 Do recurso. Esse código só é permitido em situações em que 
 Espera-se que o usuário consiga resolver o conflito 
 E reenvie a solicitação. O corpo de resposta DEVE incluir informações suficientes 
 Para o usuário reconhecer a origem do conflito. 
 Idealmente, a entidade de resposta deve incluir informações suficientes para o usuário ou agente de usuário 
 Corrigir o problema; no entanto, isso pode não ser 
 possível e não é obrigatório. 

Há, de fato, um conflito com o estado atual do recurso: o recurso está em um estado "bloqueado" e esse conflito só pode ser "resolvido" pelo usuário fornecendo suas credenciais e reenviando a solicitação. O corpo incluirá " informação suficiente para o usuário reconhecer a origem do conflito " (ele indicará que não está logado) e também incluirá " informações suficientes para o usuário ou agente do usuário para corrigir o problema "(ou seja, um formulário de login).

3
eggyal

Sua resposta:

401 Unauthorized especialmente se você não se importa ou não estará redirecionando pessoas para uma página de login 

-ou- 

302 Found para indicar que havia o recurso, mas eles precisam fornecer credenciais para serem retornados a ele. Faça isso somente se você estiver usando um redirecionamento e certifique-se de fornecer informações apropriadas no corpo da resposta.


Outras sugestões:

401 Unauthorized é geralmente usado para recursos aos quais o usuário não tem acesso depois de manipular a autenticação.

403 Forbidden é um pouco obscuro para mim com honestidade. Eu uso quando eu bloqueio recursos do nível do sistema de arquivos, e como seu post disse, "autorização não ajuda".

400 Bad Request é inadequado, já que a necessidade de login não representa uma sintaxe malformada.

0
one.beat.consumer

Eu acredito que 401 é o código de status correto para retornar da autorização falhada. Referência RFC 2616 seção-14.8 Ele lê "Um agente do usuário que deseja se autenticar com um servidor - geralmente, mas não necessariamente, após receber um 401 response" 

0
Julius Depulla