8 min de leitura

Por que endpoints GET que alteram dados precisam de aprovação de gravação?

Endpoints GET que alteram dados convidam novas tentativas, prévias e ações acidentais. Encontre gravações ocultas, reprojete contratos e exija aprovação antes do envio.

Por que endpoints GET que alteram dados precisam de aprovação de gravação?

Um endpoint GET que muda o estado é uma gravação com o uniforme errado. O perigo não é teórico. Navegadores, rastreadores, clientes de API, ferramentas de monitoramento, serviços de prévia de links, caches e agentes fazem solicitações GET extras porque o protocolo diz que isso é seguro. Se seu endpoint cancela um trabalho, rotaciona um token, envia uma mensagem ou altera um registro, essas solicitações extras podem virar ações em produção.

As equipes costumam encontrar essas rotas depois de um incidente estranho e então corrigem o único endpoint que causou o problema. Isso é limitado demais. É preciso encontrar cada chamada com aparência de leitura que muda algo, classificar o efeito pela consequência e colocá-la atrás da mesma barreira de autorização e auditoria que uma gravação explícita. Mudar o verbo importa, mas é apenas parte do conserto.

Métodos HTTP seguros descrevem a semântica solicitada

Endpoints GET que alteram dados violam a promessa por trás do método, mesmo quando seus autores tinham um motivo prático para escolher GET. A RFC 9110 define GET como um método seguro e explica que uma solicitação segura não pede ao servidor que mude de estado. A RFC permite efeitos incidentais, como registro e contabilização, porque o cliente não pediu esses efeitos. Ela não justifica uma rota cujo objetivo pretendido é fazer uma alteração.

Essa distinção revela uma desculpa comum: "O servidor precisa atualizar last_seen quando lê o item." Se o cliente pediu para recuperar um item e o serviço atualiza um carimbo interno de acesso, isso pode ser incidental. Se o cliente pediu para recuperar um item e o serviço marca uma fatura como paga, cria uma exportação, consome um token ou avança um fluxo de trabalho, a alteração é a operação solicitada. Chame isso de gravação.

A RFC 9110 também traz o motivo operacional para se importar. Agentes de usuário podem automatizar métodos seguros. Um navegador pode buscar uma página para criar uma prévia. Um rastreador pode seguir um link. Uma biblioteca cliente pode tentar de novo depois de perder a resposta. A semântica do protocolo dá a esses atores permissão para agir assim. Seu servidor não pode depender de todos os chamadores terem lido sua exceção não documentada.

Não confunda seguro com idempotente. Um DELETE pode ser idempotente porque repeti-lo mantém o recurso excluído, mas ainda é inseguro porque a primeira chamada muda o estado. Um GET que incrementa um contador uma vez por solicitação pode ser idempotente apenas no sentido restrito de parar após um limite, mas continua inseguro porque seu objetivo muda o estado. Essas palavras respondem a perguntas diferentes:

  • Seguro pergunta se o chamador solicitou uma alteração de estado.
  • Idempotente pergunta se repetir a mesma solicitação produz o mesmo efeito pretendido.
  • Armazenável em cache pergunta se um intermediário pode reutilizar uma resposta.

Quando uma equipe confunde esses termos, costuma aplicar proteção contra repetição e decidir que o problema foi resolvido. A prevenção de duplicatas ajuda. Ela não impede que um scanner de links realize a primeira ação destrutiva.

Procure consequências, não nomes de rotas suspeitos

Você encontra alterações ocultas rastreando o que uma rota causa, não confiando no nome ou no verbo. Rotas chamadas getReport e view podem enfileirar trabalho. Rotas chamadas reset podem ser perfeitamente inofensivas se retornam um formulário. Monte seu inventário combinando evidências de execução e caminhos de código.

Comece com cada manipulador registrado para GET e HEAD. Para cada manipulador, rastreie gravações diretas e repasses: transações de banco de dados, publicação em fila, invalidação de cache com significado de negócio, entrega de e-mail ou chat, pagamentos, mudanças de credenciais, exclusão de arquivos e chamadas externas a outro serviço. Um manipulador GET que chama um serviço interno pode parecer limpo em seu próprio repositório, enquanto essa chamada interna realiza a alteração. Siga-a até conseguir nomear o efeito final.

Esta busca simples encontra bastante código antigo:

rg -n 'GET|\.get\(|router\.get\(|app\.get\(' src
rg -n 'INSERT|UPDATE|DELETE|enqueue|publish|sendMail|charge|revoke|rotate' src

O formato da saída é menos útil que o registro de revisão criado a partir dela. Dê a cada descoberta um endpoint, gatilho, efeito final, sistema afetado e lista de chamadores. Não escreva "atualiza o status" na coluna de efeito. Escreva "marca a implantação d-481 como cancelada e envia o cancelamento ao agendador". Entradas vagas permitem que um revisor aprove uma ação grave sem perceber.

Dados de execução encontram o que a revisão do código não vê. Em um ambiente que não seja de produção, envie solicitações representativas com um ID de correlação. Em seguida, consulte logs da aplicação, tabelas de trabalhos, logs de chamadas externas e registros de auditoria por esse ID. Se uma solicitação GET levar a uma mensagem, alteração de linha, item de fila ou solicitação externa, registre toda a cadeia. Uma rota pode alterar algo por meio de um worker agendado alguns segundos depois, o que torna apenas o log de solicitações enganoso.

Fique atento a alterações que desenvolvedores descartam por não serem gravações em banco de dados relacional. Gerar uma URL de download de uso único consome uma capacidade. Iniciar uma exportação pode gerar uma conta alta. Ler uma rota de "aceitação de convite" pode adicionar uma pessoa a uma organização. Chamar um endpoint de relatório pode acordar um trabalho caro no data warehouse. O recurso devolvido pode ser somente leitura, enquanto a operação que o produziu não é.

APIs legadas escondem gravações em lugares conhecidos

As piores rotas legadas geralmente começaram como atalhos para uma página voltada a pessoas. Alguém facilitou o clique em um link administrativo, depois outro serviço copiou a URL, depois um script passou a depender dela, e o atalho virou um contrato de API.

Links de confirmação de redefinição de senha são um caso clássico. Uma rota como GET /reset/confirm?token=... parece conveniente porque um navegador pode abri-la. Se abrir essa URL consome o token e muda a senha, scanners de e-mail e ferramentas de prévia podem consumi-lo primeiro. O design seguro usa GET para exibir um estado de confirmação sem consumir nada, depois usa POST para enviar a confirmação. A página pode carregar uma referência de curta duração no servidor, mas a gravação acontece somente após a ação explícita.

Links de cancelamento de inscrição exigem mais cuidado, não menos. Sistemas de e-mail e leis de privacidade tornam atraente cancelar com um clique, e alguns padrões esperam isso. Se um scanner de segurança de caixa de correio seguir esse link, o destinatário pode perder uma inscrição sem tocar na mensagem. Use o mecanismo de cabeçalho padrão quando ele se aplicar, entenda como o ecossistema receptor o trata e torne o comportamento do endpoint intencional. Não copie um padrão genérico de "GET unsubscribe" para uma API administrativa sem relação e o chame de precedente.

Outros infratores recorrentes incluem:

  • GET /jobs/123/retry, que cria uma nova execução toda vez que um painel é atualizado.
  • GET /deployments/123/rollback, que uma sonda de monitoramento pode chamar ao testar links.
  • GET /tokens/123/revoke, que transforma uma URL de suporte em uma capacidade destrutiva.
  • GET /invoices/123/send, que transforma um bot de prévia em remetente de e-mails.
  • GET /reports/monthly, que inicia silenciosamente uma exportação cara em vez de retornar uma.

A recomendação popular de "basta exigir um parâmetro secreto na query" está errada. Strings de consulta se espalham pelo histórico do navegador, análises, logs do servidor, cabeçalhos Referer em alguns fluxos, capturas de tela e mensagens copiadas. Mais importante: uma URL secreta ainda é uma URL GET. Qualquer pessoa ou sistema que a receba pode ativar a ação sem uma barreira de aprovação.

Novas tentativas e prévias ampliam o impacto

Um único GET que altera estado alcança um público maior do que seu autor imagina, porque chamadores automatizados o tratam como repetível. O primeiro sintoma muitas vezes parece aleatório: uma operação acontece duas vezes, uma conta muda de um dia para outro ou um usuário vê uma ação que não realizou. Os logs de solicitação mostram credenciais legítimas, então o incidente recebe o rótulo de erro do operador. Esse rótulo muitas vezes encerra a investigação cedo demais.

Considere um endpoint legado que reinicia uma build remota ao receber GET /builds/77/retry. Um agente busca a URL por um caminho de rede que expira depois de o servidor aceitar a solicitação. O agente faz o que muitos clientes HTTP fazem e tenta novamente. A solicitação original já enfileirou a build 311; a segunda enfileira a build 312. Um painel então carrega um link de prévia no feed de atividades e enfileira a build 313. O manipulador pode retornar 200 OK sempre, portanto nada na resposta informa que a operação foi duplicada.

Um redirecionamento pode trazer outra surpresa. Se uma ação GET antiga redireciona para uma nova rota e a nova rota ainda age em GET, o redirecionamento preserva a semântica insegura. Se o redirecionamento muda o método de um modo que o cliente não espera, os clientes podem falhar de forma inconsistente. Redirecionamentos ajudam na migração, não são lugar para esconder uma mudança de autorização ou de semântica do método.

Caches tornam a falha mais estranha. Um cache compartilhado não deveria armazenar uma resposta a um GET que altera estado sem instruções explícitas, mas sistemas erram e desenvolvedores adicionam cabeçalhos de cache mecanicamente. Mesmo sem armazenamento em cache, um pré-carregador pode emitir a solicitação antes de o usuário decidir agir. Não baseie a segurança na esperança de que todo intermediário respeite sua intenção privada.

O conserto começa na barreira. Uma operação que pode mudar um sistema remoto precisa de uma solicitação de ação explícita antes que o cliente HTTP a envie. O chamador deve ver um nome de ação distinto, o alvo e a consequência. Um timeout depois do envio passa a ser uma gravação incerta, que o chamador resolve consultando o status ou usando uma chave de idempotência em vez de repetir às cegas.

Dê às ações um contrato com formato de gravação

Aprove antes do envio
O Sallyport oferece aos agentes no macOS uma barreira de aprovação humana antes que solicitações HTTP externas saiam da máquina.

Um endpoint corrigido deve expor a alteração na URI, no método, no corpo da solicitação, na resposta e na documentação. Você não precisa discutir REST de modo excessivamente centrado em substantivos para fazer isso bem. Precisa de um contrato que impeça chamadores de confundir uma ação com uma busca.

Para o exemplo da build, use um endpoint de ação POST e aceite uma chave de idempotência. O endpoint deve retornar um recurso que identifique a nova execução, não uma mensagem genérica de sucesso.

POST /v1/builds/77/retries HTTP/1.1
Idempotency-Key: 9ef8b462-97bf-4ca3-bb8b-4396a60ed9ae
Content-Type: application/json

{"reason":"retry after failed dependency download"}
HTTP/1.1 201 Created
Content-Type: application/json
Location: /v1/builds/311

{"id":"311","source_build":"77","state":"queued"}

Armazene a chave de idempotência com o principal autenticado, o tipo de ação, o alvo e o resumo da solicitação. Se o mesmo chamador enviar novamente a mesma chave e a mesma solicitação, retorne o resultado original. Se ele reutilizar a chave com um corpo ou alvo diferente, retorne um conflito. Um registro de chave compartilhado globalmente pode permitir colisão entre tenants, enquanto uma chave que ignora o corpo da solicitação pode transformar um erro de copiar e colar na ação errada.

Use PUT ou PATCH quando a solicitação descreve o estado desejado do recurso. PATCH /v1/deployments/77 com {"paused":true} pode fazer sentido quando o recurso é dono desse campo. POST /v1/deployments/77/rollback descreve melhor um comando com nova execução, trilha de auditoria e possível resultado assíncrono. Não force um comando em PATCH apenas para atender ao guia de estilo de alguém.

Retorne estado suficiente para que um chamador se recupere da ambiguidade. Se uma ação for executada de forma assíncrona, retorne um ID de operação e ofereça um endpoint GET que apenas leia seu progresso. Assim, o GET pode ser repetido, consultado periodicamente, armazenado em cache conforme seus cabeçalhos de resposta e aberto no navegador sem mudar o mundo.

A aprovação vem antes da injeção de credenciais

Aprovação depois que uma solicitação HTTP saiu da máquina é encenação. Um serviço remoto pode agir antes de o cliente receber uma resposta, e uma resposta de falha não prova que ele não fez nada. Coloque a decisão onde a solicitação é montada, antes de anexar credenciais e antes de bytes saírem do processo.

Isso importa quando um agente de programação com IA chama uma API. O agente pode inferir que uma rota é de leitura a partir da descrição de uma ferramenta, copiar uma URL antiga de um repositório ou seguir uma sugestão em um ticket. Se tiver credenciais brutas, pode fazer a chamada antes de uma pessoa ver o endpoint. Um prompt que pede cuidado ao modelo não é um controle de autorização.

Dê à camada de aprovação um modelo de ação normalizado. Ela deve incluir pelo menos o método HTTP, host, caminho, identificador do alvo quando houver e uma consequência concisa. A camada deve classificar ações pelo contrato do serviço, não apenas por method === "GET". Uma rota GET legada que chama revokeToken deve entrar no mesmo caminho de aprovação que POST /tokens/123/revoke até você removê-la.

Um mapeamento prático pode ser assim:

{
  "method": "GET",
  "url": "https://api.example.test/v1/tokens/tk_42/revoke",
  "semantic_action": "revoke credential",
  "target": "tk_42",
  "approval": "required",
  "reason": "legacy GET endpoint changes remote credential state"
}

Não mostre a quem aprova apenas um hostname e um botão verde. O aviso deve dizer que a chamada revoga uma credencial e nomear o alvo que será afetado. Se seu sistema não consegue determinar a ação semântica, trate a chamada como não classificada e exija aprovação. Permitir todos os GETs por serem GETs recria a falha original uma camada mais abaixo.

A autorização por sessão e os controles de credenciais por chamada do Sallyport podem ficar nessa barreira para agentes que usam seu canal HTTP. A escolha de design importante independe do app: quem guarda o cofre executa a solicitação, enquanto o agente recebe o resultado em vez do segredo.

Mantenha leituras úteis e gravações difíceis de disparar por acidente

Revogue uma execução suspeita
As sessões mostram execuções de agentes separadamente, para que você revogue um processo sem perder o histórico das chamadas.

O padrão de migração mais limpo preserva um GET seguro para descoberta e introduz um endpoint de gravação separado para o ato. Você pode manter uma página amigável, um endpoint de status ou uma resposta de simulação sem permitir que uma busca execute o comando.

Para um gerador de relatórios, GET /reports/monthly pode retornar o relatório concluído mais recente e o status atual da geração. POST /reports/monthly/runs inicia uma nova geração. Para uma operação de credencial, GET /tokens/tk_42 pode retornar metadados, enquanto POST /tokens/tk_42/revocations cria um evento de revogação. O segmento de caminho extra é menos engenhoso que um parâmetro de ação na query, mas deixa logs, clientes e telas de revisão muito mais claros.

Uma simulação merece um contrato preciso. POST /deployments/77/rollback?dry_run=true continua sendo POST porque o chamador pediu uma avaliação do comando, mesmo que ela não confirme nada. Retorne os alvos planejados, as pré-condições esperadas e quaisquer valores não resolvidos. Não faça GET /rollback?preview=true executar o planejamento do comando se o planejamento em si adquire bloqueios, reserva capacidade ou contata um provedor com efeito observável.

Algumas equipes tentam preservar integrações antigas fazendo o GET antigo retornar uma página HTML com um formulário que envia automaticamente um POST. Isso apenas transfere o risco para o navegador. Use uma página que exija interação real da pessoa e proteja o POST com as defesas de mesma origem adequadas à aplicação. Clientes de API devem receber uma resposta clara de descontinuação e um prazo de migração, não um documento de navegador que não conseguem usar.

Teste os chamadores que nunca pedem permissão

Uma rota não está corrigida até que você teste o comportamento automatizado que a tornou insegura. Testes unitários que afirmam que um manipulador chama um método de serviço não bastam. Exercite a rota como um navegador, um cliente HTTP sob timeout, um rastreador e um agente a encontrariam.

Para cada ação migrada, execute estas verificações em um ambiente isolado:

  1. Envie o GET antigo duas vezes e confirme que ele não pode criar duas ações. Durante uma transição, ele deve falhar com segurança, exibir apenas um estado de confirmação ou retornar uma resposta de descontinuação.
  2. Simule um cliente que perde a resposta depois do envio e então repete o POST com a mesma chave de idempotência. Confirme que o serviço retorna o ID da ação original.
  3. Busque repetidamente a URL de status segura e confirme que ela não cria trabalhos, mensagens, lançamentos contábeis ou chamadas externas.
  4. Tente a ação com uma aprovação expirada ou sessão revogada e confirme que a solicitação nunca chega ao serviço remoto.
  5. Inspecione o registro de auditoria e verifique que ele identifica a ação normalizada, não somente a rota de transporte.

Use injeção de falhas no ponto incômodo: depois que o servidor confirma a ação, mas antes de enviar uma resposta. É aí que as equipes descobrem se o cliente repete às cegas. Se a única estratégia de recuperação for "tente de novo", o contrato não deu informação suficiente ao chamador.

Teste também exemplos da documentação. Um comando curl copiado para um canal de incidente vira uma interface operacional. Se o exemplo usa GET porque cabe em uma linha, alguém vai automatizá-lo. Faça o exemplo de leitura segura e o exemplo de ação explícita serem visivelmente diferentes.

Audite o efeito e a rota

Aprove a gravação real
O Sallyport guarda a credencial da API e executa a chamada após sua aprovação.

Uma linha de auditoria que diz GET /v1/builds/77/retry 200 é uma evidência ruim. Ela registra um fato de transporte enquanto oculta o evento de negócio. Durante um incidente, a pessoa investigando ainda precisa reconstruir se a chamada iniciou uma build, repetiu uma anterior ou apenas retornou seu estado.

Registre as duas camadas. Preserve o método e a rota recebidos porque o comportamento legado importa. Junto deles, registre a ação semântica, o alvo, o processo ou principal chamador, a decisão de aprovação, a identidade da credencial sem material secreto, o ID de correlação e a referência do resultado. Para um comando assíncrono, registre o ID da operação ou do recurso resultante para que eventos posteriores se conectem à solicitação original.

Um log à prova de adulteração só é útil se você puder verificá-lo depois que a confiança se tornar questionável. Mantenha o processo de verificação separado do caminho normal de leitura da aplicação. O Sallyport projeta seus diários de sessão e de chamadas a partir de um log de auditoria criptografado, encadeado por hash e sem acesso de escrita, e sp audit verify verifica essa cadeia offline sobre texto cifrado. Essa é uma propriedade que vale exigir quando um agente tinha autoridade para afetar um sistema externo.

Não deixe a retenção de auditoria virar desculpa para registrar segredos. Corpos de solicitação muitas vezes contêm credenciais, tokens, dados pessoais ou argumentos de comando que não pertencem a um log geral de atividade. Registre uma descrição normalizada e um resumo quando precisar de evidência de integridade. Armazene material sensível apenas onde controles de acesso e regras de retenção possam protegê-lo.

Remova a exceção em vez de documentá-la para sempre

O estado final não tem rotas GET que alteram dados, mesmo que um gateway de aprovação atualmente as intercepte. Manter a exceção viva convida um novo cliente, uma URL copiada ou uma refatoração futura a ignorar a tabela de classificação. A camada de compatibilidade deve ter um responsável, um inventário de chamadores e uma data para deixar de aceitar o formato antigo.

Comece pelo endpoint que pode causar o resultado mais irreversível. Adicione um contrato POST explícito, comportamento de idempotência, classificação de aprovação e registro de auditoria no nível do efeito. Depois, torne observável toda chamada GET antiga. Quando conseguir nomear os chamadores restantes, mova-os deliberadamente em vez de quebrar uma integração oculta de surpresa.

Não aceite "nosso cliente sabe mais" como propriedade de segurança. Uma solicitação GET percorre sistemas construídos para repeti-la e inspecioná-la. Faça sua gravação parecer uma gravação antes que um desses sistemas decida ajudar.

FAQ

Uma solicitação GET pode alterar dados legalmente?

Não. GET é definido como um método HTTP seguro porque a semântica solicitada não deve alterar o estado do servidor. Um servidor ainda pode registrar a solicitação ou atualizar um cache internamente, mas um GET que exclui, reinicia, envia, cobra ou altera um registro viola o contrato em que os chamadores confiam.

Como encontro endpoints GET com efeitos colaterais?

Comece por rotas chamadas retry, cancel, reset, resend, rotate, sync, export, confirm ou preview. Depois, compare os logs de solicitação com gravações no banco de dados, enfileiramento de trabalhos, mensagens enviadas e chamadas a serviços de terceiros feitas logo após a execução dessas rotas.

Um endpoint GET que envia um e-mail deve exigir aprovação?

Trate-o como uma gravação quando o resultado solicitado altera uma conta, recurso, fluxo de trabalho, permissão, estado de cobrança, sistema externo ou entrega de mensagem. O verbo HTTP é um indício de intenção, não uma prova de que a operação é inofensiva.

Por que solicitações GET que alteram estado são perigosas com novas tentativas?

Eles podem repetir uma solicitação após um timeout, seguir um redirecionamento, pré-carregar um link, atualizar uma página ou buscar um recurso para criar uma prévia. Uma pessoa pode acreditar que clicou uma vez enquanto o servidor recebe duas ou mais solicitações.

A autenticação basta para uma solicitação GET que altera estado?

Não. A autenticação responde quem pode chamar um endpoint; a aprovação responde se essa ação específica deve acontecer agora. Uma credencial de longa duração anexada a um agente não torna aceitável uma alteração de estado inesperada.

Devo usar POST, PUT ou PATCH para uma ação GET legada?

Use um endpoint de ação POST quando a operação dispara um comando, como /jobs/{id}/cancel, e documente seu resultado. Use PUT ou PATCH quando o chamador fornece uma representação substituta ou parcial de um recurso.

Posso manter a rota GET antiga por compatibilidade?

Mantenha a rota antiga temporariamente apenas se puder medir e migrar seus chamadores. Faça-a redirecionar somente para uma página de confirmação que não altera nada, rejeite chamadores automatizados inseguros ou devolva uma resposta clara de descontinuação enquanto os clientes migram para o novo endpoint de ação.

Chaves de idempotência podem tornar endpoints GET inseguros seguros?

Sim, se cada tentativa incluir uma chave de idempotência e o serviço guardar o primeiro resultado concluído por um período adequado. Isso protege contra entrega duplicada, mas não torna um GET que altera estado seguro para rastreadores ou prévias.

O que um log de auditoria deve registrar para uma solicitação de API que altera estado?

Registre a ação normalizada, o alvo, a identidade do chamador, a decisão de autorização, o resultado e um ID de correlação. Registre também o método e a rota HTTP recebidos para que investigadores possam provar que uma solicitação antiga com formato de leitura realizou uma gravação.

Como um agente de IA pode aprovar chamadas perigosas de API sem ver credenciais?

Coloque a barreira de aprovação antes de o cliente enviar a solicitação HTTP, pois o serviço remoto pode agir antes de qualquer resposta retornar. O Sallyport pode fazer isso quando um agente usa seu canal HTTP: o agente solicita uma ação, enquanto o app injeta a credencial e registra a chamada.

Sallyport

O Sallyport executa chamadas de API e comandos SSH pelo seu agente de IA. As chaves ficam em um cofre local no seu Mac; você aprova cada execução e toda ação vai para um registro selado.

© 2026 Sallyport · Código aberto sob Apache-2.0 · Oleg Sotnikov