Por que uma solicitação GET não deve alterar os dados no servidor?

109

Em toda a internet, vejo os seguintes conselhos:

Um GET nunca deve alterar dados no servidor - use uma solicitação POST para esse

Qual é a base para essa ideia?

Se eu faço um serviço php que insere dados no banco de dados e transmito parâmetros na string de consulta GET, por que isso está errado? (Estou usando instruções preparadas, para cuidar da injeção de SQL). Uma solicitação POST é de alguma forma mais segura?

Ou há alguma razão histórica para isso? Em caso afirmativo, qual é a validade deste conselho hoje?

Devdatta Tengshe
fonte
Obrigado por fazer esta pergunta, e obrigado @Oded pela resposta bem formulada eu sempre necessária uma referência para enviar as pessoas que fazem essa pergunta em direção :)
Benjamin Gruenbaum
Ver também PUT HTTP - stackoverflow.com/questions/630453/put-vs-post-in-rest (com notas sobre ser idempotent)
Bratch
2
@JoachimSauer Embora o GET os tenha salvado do rastreador, o problema raiz é a falta de autenticação. Qualquer script infantil poderia tê-los postado no esquecimento também.
CodesInChaos

Respostas:

185

Este não é um conselho.

A GETé definido dessa maneira no protocolo HTTP . É suposto ser idempotente e seguro .

Quanto ao porquê - a GETpode ser armazenado em cache e em um navegador, atualizado. De novo e de novo e de novo.

Isso significa que, se você fizer o mesmo GETnovamente, será inserido novamente no seu banco de dados .

Considere o que isso pode significar se ele se GETtornar um link e for rastreado por um mecanismo de pesquisa. Você terá seu banco de dados cheio de dados duplicados.

Sugiro também a leitura de URIs, endereçamento e o uso de HTTP GET e POST .


Há também um problema com a ligação prefetching em alguns navegadores - eles vão fazer uma chamada de pré-busca links, mesmo se não for indicada por isso, o autor da página.

Se, digamos, seu logout estiver protegido por um "GET", vinculado a todas as páginas do site, as pessoas poderão se desconectar apenas devido a esse comportamento.

Oded
fonte
35
Muitas, muitas, muitas ferramentas, utilitários, rastreadores da Web e outras coisas importantes assumem que GETnunca será uma ação destrutiva (com razão, uma vez que é especificada dessa maneira). Se você agora interromper seu aplicativo quebrando essa especificação, poderá manter as duas partes do aplicativo.
Joachim Sauer #
7
@NimChimpsky: é alterado por um GET. Esse conselho está simplesmente errado. Seguro significa que o usuário não pode ser responsabilizado por efeitos colaterais, não que não possa haver efeitos colaterais. Caso contrário, você não poderia ter arquivos de log para o seu servidor, o que seria um absurdo! Isso é explicado claramente na seção 9.1.1 da RFC2616.
Jörg W Mittag 01/03
8
@ JörgWMittag: Eu não diria "simplesmente errado", diria "redigido de maneira imperfeita". Um GET não deve mudar, pois é objetivo. É claro que você pode contar, registrar e observar uma solicitação GET. Mas não deve modificar seus dados comerciais reais.
Joachim Sauer
23
@NimChimpsky A GETnão deve alterar o recurso solicitado pelo GET, mas isso não significa que 'nada no servidor deve mudar'. É claro que coisas como logs, contadores e outro estado do servidor podem mudar durante qualquer solicitação.
Eric Rei
8
Há alguns anos, o Google lançou um complemento de navegador (iirc) que buscava previamente as páginas por meio de links. Isso também aconteceu em alguns painéis de controle mal projetados - os URLs causariam um registro ou algo a ser gravado ou até excluído no servidor (pense em post? Action = delete). Isso fez com que as ações fossem executadas sem que o usuário soubesse. O Google interrompeu esse complemento por esse motivo, iirc, mesmo que tenha sido culpa do fabricante do aplicativo de web usar os GETs para alterar o estado.
Cthulhu
24

Cada verbo HTTP tem sua própria responsabilidade. Por exemplo GET, conforme definido pela RFC

significa recuperar qualquer informação (na forma de uma entidade) identificada pelo Request-URI.

POST, por outro lado, significa inserir ou mais formalmente

O método POST é usado para solicitar que o servidor de origem aceite a
entidade incluída na solicitação como um novo subordinado do recurso
identificado pelo Request-URI na linha de solicitação

Razões para mantê-lo desta maneira:

  • É muito simples e funciona na escala global da Internet desde 1991
  • Atenha-se ao princípio da responsabilidade única
  • Outras partes costumam GETatuar como meio de recuperação de informações e mineração de dados
  • Presume-se que GET é uma operação segura que nunca modifica o estado do recurso
  • Considerações de segurança, GETé efetivamente uma leitura , enquanto POSTé efetivamente uma gravação
  • GET é armazenado em cache por navegadores, nós na rede, provedores de serviços de Internet
  • A menos que o conteúdo seja alterado, GETo mesmo URL deve retornar os mesmos resultados para todos os usuários, caso contrário você não confiará mais no resultado retornado

Para garantir a integridade e apenas para impor o uso correto (origem) :

  • GETos parâmetros são passados ​​como parte da URL, que possui um tamanho pequeno e limitado de 256 caracteres por padrão, com alguns servidores suportando mais de 4000 caracteres. Se você deseja inserir um registro longo, não há maneira legítima de transmitir esses dados em
  • Ao usar conexão segura, ̶ como o TLS, ̶ URL é não conseguir criptografado, ̶, logo, todos os parâmetros do ̶ ̶G̶E̶T̶̶ são transferidos Texto simples. O URL é realmente criptografado com TLS, portanto, o TLS está correto.
  • Inserir dados binários ou caracteres não ASCII usando GETé impraticável
  • GET é reexecutado se um usuário pressionar um botão Voltar em um navegador
  • Alguns rastreadores mais antigos podem não indexar URLs com um ?sinal dentro
oleksii
fonte
1
Tem certeza de que o URL não está criptografado pelo TLS? Fiquei com a impressão de que os handshakes SSL / TLS ocorrem antes da transferência dos cabeçalhos HTTP. Essa é a razão pela qual a hospedagem virtual de sites HTTPS em um único endereço IP é difícil. Estou enganado?
Brandon
É isso mesmo, eu
consertei
2
Os navegadores @Brandon Modern enviam o domínio do host como parte do handshake TLS (conhecido como indicação do nome do servidor), para permitir a hospedagem de mais de um domínio por endereço IP. A parte do caminho / consulta do URL é protegida pelo TLS. Não há diferença entre GET e outros verbos HTTP a esse respeito.
CodesInChaos
9

EDIT: Antes, eu disse que o POST ajuda a protegê-lo contra o CSRF, mas isso está errado. Eu não pensei nisso corretamente. Você deve exigir um token oculto exclusivo do escopo da sessão em todas as suas solicitações para alterar os dados para se proteger contra o CSRF.

Nos primeiros dias da internet, havia aceleradores de navegador. Esses programas começariam a clicar nos links de uma página para armazenar em cache o conteúdo. O Google Web Accelerator foi um desses programas. Isso pode causar estragos em um aplicativo que faz alterações quando um link é clicado. Eu assumiria que ainda existem pessoas usando software acelerador.

Servidores e navegadores proxy armazenam em cache as solicitações GET, portanto, quando o usuário acessa a página novamente, ele pode não enviar a solicitação ao seu aplicativo, para que o usuário pense que tomou uma ação, mas na verdade não o fez.

Sarel Botha
fonte
1
O CSRF é igualmente possível com GET e POST. Por exemplo, o invasor pode incluir um formulário de envio automático no site para acionar uma solicitação POST. A abordagem padrão para impedir o CSRF é incluir explicitamente um valor desconhecido para o invasor na solicitação (ao contrário do que inclui implicitamente os cabeçalhos dos cookies).
CodesInChaos
8

Se eu faço um serviço php que insere dados no banco de dados e transmito parâmetros na string de consulta GET, por que isso está errado?

A resposta mais simples é "porque não é isso que GETsignifica".

Usar GETpara passar dados para uma atualização é como escrever uma carta de amor e enviá-la em um envelope marcado como "OFERTA ESPECIAL - Aja AGORA!" Nos dois casos, não se surpreenda que o destinatário e / ou os intermediários manuseiem incorretamente sua mensagem .

Nathan Long
fonte
5

Para suas operações CRUD em um aplicativo centralizado em banco de dados, use o seguinte esquema:

Usar HTTP GET para operações de leitura (SQL SELECT)

Use HTTP PUT para operações de atualização (SQL UPDATE)

Usar HTTP POST para criar operações (SQL INSERT)

Use HTTP DELETE para operações de exclusão (SQL DELETE)


fonte
3
Put vs post não é como você afirma. Put é para quando o cliente estiver modificando o recurso no local exato fornecido. Para uma postagem, o servidor finalmente decide o Uri exato para o recurso.
213 Andy
HTTP PUT não é mais como um SQL DELETE e INSERT em vez de UPDATE? Além disso, o SQL UPDATE pode atualizar muitos registros de uma só vez, mas o HTTP PUT atualizará apenas uma coisa.
Backwards_Dave
0

Um GET nunca deve alterar dados no servidor - use uma solicitação POST para esse

Esse conselho, e todas as respostas aqui estão erradas. Obviamente, estou sendo excessivamente dramático, as outras respostas são excelentes, mas acredito que o conselho exato deve ser dado como:

Um GET raramente deve alterar dados no servidor - use uma solicitação POST para esse

Dizer "nunca" é muito extremo e, embora as outras respostas aqui expliquem com precisão por que você "raramente" deve fazê-lo, há alguns cenários em que é perfeitamente razoável alterar dados com um GET. Um exemplo é um link de verificação de e-mail de uso único. Normalmente, esses links contêm um GUID que, quando acessado, terá que alterar dados. Se implementadas corretamente, solicitações GET idênticas subsequentes serão ignoradas.

Obviamente, este é um caso extremo, mas certamente vale a pena notar.

TTT
fonte
3
E se o seu cliente de email decidir buscar o link sem clicar nele? Por exemplo, porque ele deseja verificar se há malware. A abordagem adequada para os links de cancelamento de inscrição é levar a uma página na qual o usuário pode clicar em um botão para cancelar a inscrição (onde o clique no botão aciona uma solicitação POST).
CodesInChaos
@CodesInChaos - excelente ponto! Eu concordo com você. Eu removi o exemplo de cancelamento de inscrição e deixei a verificação por email como o único exemplo. Pode haver outros, além da verificação de e-mail, nos quais um GET faz sentido, mas não consigo pensar em nenhum no momento.
TTT
O problema de GET ter efeitos colaterais se aplica igualmente à confirmação por email. Agora, o cliente que segue o link confirmaria uma conta criada por alguém usando seu e-mail, permitindo que ele se faça passar por você.
CodesInChaos
@CodesInChaos - esse é um trecho. A representação de que você fala viria do mesmo nome de usuário ou nome pessoal público, não do mesmo endereço de email, e isso pode acontecer independentemente do endereço de email que eles usam (normalmente, apenas o servidor sabe o endereço de email do titular da conta). Além disso, seria inútil criar uma conta com o endereço de e-mail de outra pessoa. Como isso poderia ajudá-los? Eles não podiam controlar sua própria conta.
TTT