Um chatbot de suporte para o tenant acme recebe um chamado solicitando um reembolso. O solicitante envia apenas texto de chat; no entanto, a assist-api permite que o modelo gere uma solicitação payments.refund e a envia utilizando as credenciais de pagamento abrangentes do serviço. O backend emite uma proposta de reembolso que o usuário u_123 nunca teve autorização para solicitar. Não foi necessário nada incomum: texto não confiável passou da mensagem do solicitante para uma ação, enquanto o modelo não tinha autoridade para decidir se tal ação era permitida.
Um guardrail (mecanismo de proteção) é um ponto de controle que restringe o que uma aplicação LLM aceita, consome, retorna ou executa. Nesse cenário de falha, o guardrail útil mais simples rejeitaria a solicitação de reembolso, a menos que a entidade autenticada possuísse o escopo necessário e uma etapa de aprovação no servidor aceitasse os parâmetros reais do reembolso. A redação do prompt não oferece essa proteção, pois o modelo apenas segue o texto que recebe; ele não autentica solicitantes, gerencia orçamentos nem autoriza o uso de ferramentas.
O sistema em operação possui um fluxo que manteremos inalterado. O usuário final u_123 pertence ao tenant acme e chama a assist-api por meio do llm-gateway. O serviço FastAPI monta um prompt, chama o provedor providerA na rota chat usando $CHAT_MODEL e pode solicitar uma ação de ferramenta. O usuário u_123 possui permissões para chat:invoke e payments:refund:propose, mas não autoridade irrestrita para pagamentos.
O contrato de pagamento permanece restrito e explícito. A única ferramenta de pagamento é a payments.refund, que utiliza invoice_id (como inv_987), amount_cents como um número inteiro (como 1299) e reason como uma string. Uma proposta de reembolso não equivale a uma permissão para executá-lo. O servidor verifica a propriedade do tenant, o escopo, os argumentos e a aprovação antes que qualquer ação de pagamento irreversível seja realizada.
Qualquer limite que envolva a transmissão de texto é considerado não confiável, incluindo a mensagem do solicitante, documentos recuperados, resultados de ferramentas e a saída do modelo. Um documento de suporte recuperado pode conter instruções que conflitam com a política da aplicação, e a saída do modelo pode conter campos que se tornariam inseguros se passados para uma API de pagamento sem validação. Esse cenário corresponde à vulnerabilidade LLM01 (Prompt Injection) da lista OWASP Top 10 for LLM Applications 2025, publicada pelo projeto OWASP GenAI Security em novembro de 2024. A regra fundamental é clara: nenhuma chamada de ferramenta pode ser executada com mais autoridade do que aquela conferida pelo usuário u_123.
[fontes][/fontes]
O caminho fixo coloca as entradas não confiáveis e os limites de imposição em uma única visualização.
O gateway identifica quem fez a chamada, enquanto a assist-api aplica as regras de tenant (inquilino), escopos, contrato da ferramenta e requisitos de aprovação do usuário. O provedor providerA gera uma saída usando suas próprias credenciais de provedor, mas essa credencial nunca deve conferir autoridade para realizar reembolsos em nome de u_123. Usar as permissões mais amplas do serviço para um usuário que não as possui cria uma vulnerabilidade do tipo “deputado confuso” (confused deputy).
Regra de limite
O modelo pode sugerir
payments.refund; apenas verificações no lado do servidor podem autorizar ou executar essa ação.
Como cada limite tem uma função diferente, uma recusa deve identificar a etapa que não pôde prosseguir com segurança. O llm-gateway retorna 401 Unauthorized (Não Autorizado) para credenciais ausentes, inválidas, revogadas ou comprometidas. Após a autenticação, o gateway ou a aplicação retorna 403 Forbidden (Proibido) quando o solicitante não possui um escopo necessário. Um destino bloqueado para a busca de uma ferramenta também resulta em 403 Forbidden, e o destino bloqueado nunca chega a ser contatado.
Após a resposta do providerA, a assist-api valida a saída do modelo antes que ela seja recebida por um parser, pela API de pagamentos ou por outro componente subsequente. Saídas com esquema inválido retornam 422 Unprocessable Content (Conteúdo Não Processável) — a frase da RFC 9110 que substitui o termo Unprocessable Entity (Entidade Não Processável) usado em documentações mais antigas. A reutilização de uma chave de idempotência com parâmetros de reembolso diferentes também retorna 422 Unprocessable Content; reutilizar a mesma chave enquanto a primeira solicitação ainda está ativa retorna 409 Conflict (Conflito).
Os resultados restantes descrevem um limite operacional, e não uma decisão do modelo.
202 Accepted (Aceito) é retornado quando um reembolso irreversível fica retido para aprovação, acompanhado de um identificador de aprovação pendente.429 Too Many Requests (Muitas Solicitações), com o cabeçalho Retry-After, é retornado quando uma cota compartilhada ou um limite de taxa (rate limit) é esgotado.503 Service Unavailable (Serviço Indisponível) é retornado quando o armazenamento de políticas de controle está indisponível e a aplicação não consegue aplicar a política com segurança.504 Gateway Timeout (Tempo Limite do Gateway) é retornado quando o providerA excede o tempo limite definido para a chamada ao provedor.Esses resultados impedem que os clientes tratem uma recusa apenas como uma falha vaga do modelo. Eles também indicam onde investigar: se o controle ausente diz respeito à identidade no llm-gateway, à validação na assist-api ou a uma chamada de provedor que não foi concluída.
Na falha inicial, o atacante possui apenas uma mensagem de chat, mas provoca uma proposta de reembolso irreversível. Qual etapa deve recusar a solicitação primeiro: A, autenticação do chamador e vinculação ao tenant no llm-gateway; B, autorização e aprovação de ferramentas na assist-api; ou C, verificações de saída após o modelo?
[fontes][/fontes]