Segurança de AI agents: permissões, sandboxes e ameaças de MCP
Tradução automática Este artigo foi traduzido automaticamente a partir da versão original em inglês.
Atualização do artigo
Publicado originalmente em 20 de abril de 2026. Revisto e atualizado em 6 de setembro de 2026. A atualização abrange novos controlos de sandbox, intervenções de segurança dos fornecedores e conclusões de segurança publicadas, bem como as respetivas limitações e ligações para as fontes.
A segurança de um agente começa quando um modelo propõe uma ação e antes de a máquina a executar. Decida qual é a verificação com a palavra final antes de a ação chegar a credenciais, ficheiros, redes ou a um sistema externo.
O harness é o código que constrói cada prompt, decide quais as tool calls propostas que são executadas e devolve os resultados ao modelo. A maioria das verificações deve estar aí, porque esse é o último ponto barato onde se pode parar um comando. Depois de um comando ser executado, a sandbox, as credenciais que recebeu e qualquer processo de recuperação têm de conter os danos. Alguns incidentes descritos neste artigo nem sequer chegam a um modelo.
A segurança de agentes de IA é mais abrangente do que a segurança de LLMs. Os primeiros produtos de guardrails inspecionavam a entrada e a saída de uma única chamada ao modelo. Podiam filtrar texto tóxico, remover dados pessoais, bloquear jailbreaks e rejeitar respostas fora do tópico. Esse limite era útil enquanto o modelo só podia devolver texto.
Os ciclos de tool calls acrescentaram sistemas de ficheiros, shells, servidores do Model Context Protocol (MCP) e credenciais. Isso alargou o modelo de ameaças, passando de texto inseguro para ações inseguras. Os sete grupos de incidentes abaixo abrangem injeção indireta de prompts e falhas de configuração, identidade e distribuição de software. A análise de texto pode ajudar com algumas entradas maliciosas; não pode substituir os controlos nesses pontos de execução.
Quando um agente pode ler um repositório, chamar uma ferramenta ou enviar dados a terceiros, associe cada ação proposta à verificação que a pode impedir. As secções abaixo abordam permissões, hooks, sandboxes, credenciais e revisão humana.
Em resumo: Os filtros de conteúdo inspecionam o texto em torno de uma chamada ao modelo. A segurança de agentes também controla tool calls propostas, credenciais, ficheiros, acesso à rede e efeitos irreversíveis. Vários incidentes contornaram completamente o modelo; outros usaram texto malicioso para orientar operações permitidas. Uma stack de políticas prática combina permissões, hooks anteriores às ferramentas, sandboxes do sistema operativo, escalamento para revisão humana, credenciais com âmbito limitado e um registo de auditoria.
Para consultar a lista de verificação curta de controlos, veja Lista de verificação de segurança de agentes de IA.
Stack de segurança de agentes de IA
Nenhum guardrail protege um agente por si só. Cada componente do sistema precisa da sua própria verificação.
A tabela indica onde é executada cada verificação. O harness é o programa de controlo descrito acima. O runtime é a infraestrutura que utiliza: a sandbox, o registo da sessão, o armazenamento de checkpoints e os traces que persistem após o reinício de um worker.
| Camada | O que controla | Exemplo de falha que deteta | Onde reside |
|---|---|---|---|
| Filtros de conteúdo | Texto de entrada e de saída inseguro | Saída tóxica, fuga de PII, completions que violam políticas | Harness |
| Escada de permissões | Que ferramentas, paths, APIs e scopes o agente pode usar | Um sumarizador a tentar escrever em sistemas de produção | Harness |
| Hook de política pré-ferramenta | Se esta ação específica deve ser executada agora | Comando de shell construído a partir de conteúdo recuperado não fidedigno | Harness |
| Sandbox | Aquilo a que a ferramenta pode aceder ao nível do sistema operativo e da rede | Exfiltração de ficheiros, comprometimento de dependências, command injection | Runtime |
| Verificação de aprovação humana | Ações irreversíveis ou de elevado impacto | Enviar um email, transferir dinheiro, fazer deploy para produção | Harness |
| Scoping de MCP e tokens | Para que servidor e audiência uma credencial é válida | Reutilização de um token num servidor de ferramentas não pretendido | Runtime |
| Trace de auditoria | O que aconteceu, quem aprovou e porquê | Investigação de um incidente após uma execução autónoma longa | Runtime |
Os filtros de conteúdo perguntam se o modelo disse algo inseguro. A segurança de agentes também pergunta se o sistema pode executar a ação seguinte.
As linhas do harness decidem se uma ação pode ser executada. As linhas do runtime impõem limites definidos antecipadamente e registam o que aconteceu. Mantenha a sandbox mesmo quando as regras de permissões parecem completas: pode bloquear uma chamada que o harness não previu. Não pode decidir se uma ação permitida era a ação certa; o harness tem de o fazer.
A última coluna indica onde uma verificação é executada, não quem a opera. Um fornecedor pode disponibilizar um filtro de conteúdo, mas é o harness que o invoca.
Os filtros de conteúdo geridos abrangem a camada de texto. Os restantes controlos pertencem à política da aplicação, à identidade e à infraestrutura.
Por que motivo a segurança de agentes de IA é diferente da segurança de LLMs
Bharani Subramaniam e Martin Fowler estabeleceram o enquadramento no início de 2025 em Emerging Patterns in Building GenAI Products. A observação deles era restrita e direta:
“Com sistemas tradicionais, podíamos avaliar a correção sobretudo através de testes… Com sistemas baseados em LLMs, deparamo-nos com um sistema que já não se comporta de forma determinística.”
A avaliação da saída pergunta se a resposta de um modelo cumpre uma rubrica. Um threat model de um agente tem também de abranger tool calls, comandos de shell, escritas em ficheiros, credenciais e pedidos de rede. Um output grader não consegue impedir essas ações. O harness consegue: é o conjunto de verificações que transforma uma proposta do modelo numa ação permitida. O resto deste artigo aborda essas verificações.
Simon Willison cunhou a forma do risco específico dos agentes em junho de 2025 com a tríade letal:
“A tríade letal de capacidades é: acesso aos seus dados privados; exposição a conteúdo não fiável; e capacidade de comunicar externamente de uma forma que possa ser usada para roubar os seus dados. Se o seu agente combinar estas três funcionalidades, um atacante pode facilmente induzi-lo a aceder aos seus dados privados e a enviá-los para esse atacante.”
Muitos agentes úteis combinam estas capacidades: acesso à caixa de entrada, recolha de informação na Web e uma ferramenta de mensagens; ou acesso a repositórios, leitura de issues e escrita de pull requests. Um guardrail de conteúdo verifica se o modelo gerou texto inseguro. A tríade verifica se uma entrada não fiável pode orientar o sistema para divulgar dados através de uma ação permitida.
A versão estrutural do mesmo argumento encontra-se no preprint Parallax, de Joel Fokou (arXiv 2604.12986, submetido em 14 de abril de 2026, sem revisão por pares). A afirmação central é:
“O sistema que raciocina sobre ações tem de estar estruturalmente impossibilitado de as executar, e o sistema que executa ações tem de estar estruturalmente impossibilitado de raciocinar sobre elas, com um validador independente e imutável entre ambos.”
Não é necessário aceitar os resultados da avaliação do artigo para analisar o seu argumento estrutural. Vários harnesses atuais implementam partes da mesma separação:
- Hooks PreToolUse do Claude Code
- Executor com sandbox ao nível do sistema operativo do Codex CLI (em Linux, bubblewrap e filtragem de chamadas de sistema com seccomp)
- Managed Agents da Anthropic, que mantêm as credenciais num vault ao qual o agente nunca tem acesso
- Tokens com audience binding do RFC 8707 do MCP
Estes sistemas mantêm o modelo separado do código que executa comandos. Os seus controlos são diferentes, mas nenhum permite que o executor de comandos dependa da opinião do modelo sobre segurança.
Existe uma disciplina complementar que Alessandro Pignati definiu de forma particularmente clara em janeiro de 2026: o Princípio da Agência Mínima. O Least Privilege pergunta a que pode esta identidade aceder? O Least Agency pergunta o que pode este agente decidir? O privilégio limita as credenciais; a agência limita o alcance de um plano mesmo quando as credenciais são válidas. A Excessive Agency tem a sua própria entrada no Top 10 para Aplicações LLM publicado pela OWASP, a Open Worldwide Application Security Project. A lista separada sobre sistemas agentic, abordada mais adiante neste artigo, divide a mesma falha entre uso indevido de ferramentas e abuso de privilégios. O Least Agency é a disciplina de design que previne ambos. Um agente que consegue resumir a sua caixa de entrada provavelmente não precisa de permissões de commit no seu monorepo. Continuamos a encontrar configurações em que isso acontece.
O que os guardrails de LLM abrangem
Os guardrails de LLM desempenham um papel importante em torno da chamada ao modelo. Inspecionam a entrada, o texto recolhido e a saída e, em seguida, bloqueiam, redigem, corrigem ou sinalizam o conteúdo que não cumpre uma regra configurada. Os produtos abaixo diferem quanto à implementação e à cobertura. Uma verificação de conteúdo é distinta de uma verificação de autorização no limite da ferramenta ou do servidor MCP; alguns produtos também disponibilizam funcionalidades de runtime para políticas, que exigem configuração e avaliação próprias.
NVIDIA NeMo Guardrails
A opção mais opinativa: uma framework de orquestração em torno de cinco tipos de rails (input, dialog, retrieval, execution e output), com a sua própria DSL — Colang, uma linguagem semelhante a Python para fluxos de diálogo, intenções do utilizador e mensagens do bot. Pode controlar o essencial a partir de Python + YAML, mas a lógica de diálogo mais rica é escrita em Colang — daí ser “opinativa”. Documentação em docs.nvidia.com/nemo/guardrails.
Esta é uma forma ilustrativa da API; requer o pacote e um diretório ./config configurado.
from nemoguardrails import LLMRails, RailsConfig
config = RailsConfig.from_path("./config")
rails = LLMRails(config)
response = rails.generate(
messages=[{"role": "user", "content": "Hello"}]
)
O repositório do NeMo é explícito quanto ao seu modelo de ameaças: “vulnerabilidades comuns de LLMs, como jailbreaks e prompt injections.” Também é igualmente explícito quanto ao seu âmbito: “As guardrails incorporadas podem ou não ser adequadas a um determinado caso de uso em produção… os developers devem trabalhar com a equipa interna da aplicação para garantir que as guardrails cumprem os requisitos.” O caminho de filtragem de conteúdo aqui apresentado monitoriza aquilo que o modelo diz. A documentação atual do NeMo também descreve execution rails, custom actions e inspeção de tool calls; estes são controlos de runtime configuráveis, não uma prova de que a ferramenta ou o servidor MCP em produção autenticou e autorizou a chamada. Essa fronteira continua a ser da responsabilidade da aplicação.
Meta Llama Guard 4
Um classificador de conteúdo puro de 12B, derivado por pruning do Llama-4-Scout e alinhado com a taxonomia de perigos da MLCommons (13 categorias de danos, além de abuso do code interpreter, segundo o model card). A Meta é invulgarmente clara quanto às limitações:
“A avaliação completa de algumas categorias de perigos pode exigir conhecimento factual e atualizado… Por fim, enquanto LLM, o Llama Guard 4 pode ser suscetível a ataques adversariais ou ataques de prompt injection que possam contornar ou alterar a utilização pretendida: consulte o Llama Prompt Guard 2 para detetar ataques a prompts.”
A Meta disponibiliza um produto separado para defender o seu classificador de conteúdo contra prompt injection. Se esta frase lhe parece uma admissão estrutural, é porque é.
Guardrails AI
Um registry de validators. Compõe Hub validators (PII através do Presidio, JailbreakDetect, CompetitorCheck e verificações de proveniência) com ações on_fail exception | fix | fix_reask | filter | refrain | reask | noop ou um callback personalizado (guardrailsai.com). Tenha em atenção exception, não raise. No código-fonte atual, uma string on_fail não reconhecida chega ao tratamento de callbacks personalizados e gera um erro durante a configuração do validator, em vez de emitir um aviso e recorrer a um fallback. Fixe a versão que coloca em produção e teste esse caminho de falha. Não existe um modelo de ameaças unificado; a cobertura corresponde à união dos validators instalados. Obtém proteção para tudo aquilo para que tenha um validator e nenhuma para o resto.
Lakera Guard
A API SaaS incumbente, treinada com dezenas de milhões de amostras de ataques recolhidas do Gandalf. Promete filtrar o input e o output para detetar “ataques a prompts… e fuga de dados”. O produto separado AI Agent Security da Lakera também descreve a aplicação de políticas e controlos de runtime sobre aquilo a que os agentes podem aceder, o que podem chamar e o que podem fazer. Trata-se de uma superfície de produto diferente da chamada de filtragem de conteúdo aqui abordada. Verifique o contrato atual do produto e dos preços antes da implementação.
AWS Bedrock Guardrails
A opção empresarial predefinida se já estiver a usar o Bedrock. ApplyGuardrail funciona com qualquer modelo, no Bedrock ou não:
# No-run: illustrative AWS request; requires boto3, AWS credentials, and a real guardrail identifier.
import boto3
brt = boto3.client("bedrock-runtime")
resp = brt.apply_guardrail(
guardrailIdentifier="gr-xxxxxxxxxxxx",
guardrailVersion="2",
source="INPUT",
content=[{"text": {"text": "user question",
"qualifiers": ["guard_content"]}}],
)
Preços publicados em ApplyGuardrail pricing: $0.15 por 1.000 unidades de texto para filtros de conteúdo ou tópicos recusados, e $0.10 para filtros de PII ou contextual grounding. Uma unidade de texto tem até 1.000 caracteres.
Azure AI Content Safety
Disponibiliza Prompt Shields como um endpoint unificado que «deteta e bloqueia ataques de utilizadores com input adversarial… ameaças diretas e indiretas». A Azure também é transparente: «Não pode usar o Azure AI Content Safety para detetar imagens ilegais de exploração infantil», e a qualidade multilingue está limitada a oito idiomas avaliados.
OpenAI Moderation e OpenAI Guardrails
omni-moderation-latest é a baseline multimodal gratuita. Separadamente, openai-guardrails-python (documentação em guardrails.openai.com) é a resposta da OpenAI em termos de framework: um pipeline em três fases (pre-flight, input e output), com deteção de jailbreaks, deteção de alucinações através do FileSearch, NSFW, PII através do Presidio e LLM-as-judge. GuardrailAgent integra-se com o Agents SDK.
# No-run: illustrative OpenAI Guardrails API shape; requires the package and guardrail_config.json.
from guardrails import GuardrailsOpenAI, GuardrailTripwireTriggered
client = GuardrailsOpenAI(config="guardrail_config.json")
try:
resp = client.responses.create(model="<your-model-id>", input="...")
except GuardrailTripwireTriggered as e:
print(f"blocked: {e}")
O que os filtros de conteúdo não decidem
Duas observações aplicáveis aos sete.
Em primeiro lugar, há poucos dados publicados sobre latência e throughput. Bedrock, Azure e Lakera publicam preços, mas não garantias para a latência no pior caso. A Meta também não publica qualquer garantia para endpoints alojados do Llama Guard. A NVIDIA disponibiliza o NeMo Guardrails como software alojado por si, pelo que a latência depende do modelo e da infraestrutura. Meça cada verificação síncrona no critical path, em vez de inferir o respetivo custo a partir dos preços do produto.
Em segundo lugar, esta secção abrange as configurações centradas no conteúdo listadas acima.
Um filtro de conteúdo pode inspecionar o input e o output do modelo. Não demonstra que uma chamada de ferramenta específica está autorizada, que um servidor MCP autenticou o respetivo caller, ou que o sistema consegue impedir a exfiltração de dados ou a execução de código em várias etapas antes de uma chamada ao modelo. A autorização é uma decisão separada sobre se esta identidade pode efetuar esta chamada para este servidor.
O NeMo também documenta execution rails e inspeção de chamadas de ferramentas, e a Lakera descreve a aplicação de políticas em runtime no seu produto separado AI Agent Security. Estes são controlos adicionais que é necessário configurar e testar. O resto deste artigo aborda as verificações em torno da execução de ferramentas.
Ameaças à segurança de agentes de IA: sete incidentes e o OWASP ASI Top 10
A diferença entre filtrar texto e proteger a execução deixou de ser meramente académica em meados de 2025. Os sete incidentes abaixo chegaram à retrieval, à configuração, às credenciais, à instalação de pacotes ou à execução em CI. Um classificador de conteúdo pode continuar a detetar uma string suspeita, mas os controlos que bloqueiam diretamente estes caminhos situam-se nos limites das ferramentas, da identidade, da sandbox e da supply chain.
EchoLeak — CVE-2025-32711
Divulgado em junho de 2025 pela Aim Labs, o braço de investigação da Aim Security, contra o Microsoft 365 Copilot. O relatório técnico está agora alojado na Cato Networks, que adquiriu essa equipa, sob a autoria do antigo responsável da Aim Labs, Itay Ravia (relatório). Um email criado para o efeito, redigido como instruções para o destinatário humano, passou pelo XPIA (o filtro integrado da Microsoft que procura ataques de prompt injection nos inputs do Copilot). A partir daí, foi incorporado na camada de retrieval do Copilot, a parte do sistema que pesquisa os seus documentos para encontrar contexto para as respostas. Os investigadores chamam a este truque RAG-spraying: o atacante utiliza vários emails ou um email longo, dividido em chunks, para ampliar a exposição ao retrieval. Isso aumenta a probabilidade de retrieval, mas não o garante. Uma vez lá dentro, o Copilot incorporou obedientemente os dados mais sensíveis da sessão num link Markdown que apontava para uma imagem num domínio controlado pelo atacante. A API de pré-visualização do Teams, executada num domínio que as políticas de browser da própria Microsoft já consideravam fidedigno, obteve automaticamente esse URL de imagem e, ao fazê-lo, entregou os dados ao atacante. Zero cliques. A Aim Labs deu a esta classe de ataque o nome “LLM Scope Violation”: o modelo atravessa um limite que nunca deveria atravessar, utilizando apenas operações que cada sistema individual considerava legítimas.
Cada passo parecia legítimo quando analisado isoladamente. O email era dirigido a uma pessoa. O retrieval obteve um documento que deveria obter. O link Markdown foi renderizado da forma habitual. A obtenção da imagem foi feita a partir de um domínio allowlisted. Os investigadores contornaram a filtragem do XPIA, e o modelo seguiu instruções indiretas provenientes do conteúdo obtido. O caso combina uma falha de prompt injection com o comportamento de retrieval, rendering e egress; não demonstra que os detetores não tivessem nada a assinalar.
Amazon Q Developer VS Code v1.84.0 — julho de 2025
A AWS distribuiu uma build comprometida depois de um atacante fazer commit de um ficheiro malicioso de system prompt através de um token do GitHub do CodeBuild com permissões excessivas (aviso). O payload tentou alterar as instruções do agente para o levar a executar ações destrutivas. O código malicioso foi distribuído com a v1.84.0, mas não foi executado devido a um erro de sintaxe. A AWS revogou as credenciais, removeu o código e lançou a v1.85.0. O payload falhou devido a esse erro de sintaxe, não porque um controlo de segurança o tenha bloqueado.
Azure Web Apps MCP service — CVE-2026-32211
O registo CVE do fornecedor da Microsoft diz respeito à ausência de autenticação no serviço MCP alojado do Azure Web Apps. Não se trata de um aviso contra todos os servidores MCP ou SDKs locais do Azure. Um chamador que consiga alcançar um serviço de ferramentas sem autenticação pode contornar completamente o modelo; o serviço implementado tem de autenticar e autorizar o pedido.
Vulnerabilidades de confiança em projetos do Claude Code
Estas foram vulnerabilidades distintas, não passos necessários de um único ataque:
- Foi corrigido na versão 1.0.87 um bypass do aviso de confiança.
- A execução antes da confiança, CVE-2025-59536, foi corrigida na versão 1.0.111. A configuração do repositório podia desencadear a execução antes de o projeto ser considerado fidedigno.
- A exposição do endpoint/chave de API, CVE-2026-21852, foi corrigida na versão 2.0.65. Uma configuração não fidedigna podia redirecionar o tráfego da API e expor credenciais.
Confiar no projeto, executar hooks e configurar endpoints são controlos do host. Um classificador de conteúdo não consegue impedir código que seja executado antes da chamada ao modelo.
Axios 1.14.1 e 0.30.4 — 31 de março de 2026
O post-mortem do maintainer identifica duas releases maliciosas, 1.14.1 e 0.30.4, que continham a dependência plain-crypto-js@4.2.1. Essa dependência instalava um trojan de acesso remoto: malware que permite a um atacante aceder remotamente à máquina. A exposição exigia resolver as versões afetadas e executar o comportamento de instalação relevante; um npm install não relacionado não as obtinha automaticamente. Trata-se de uma falha de execução na supply chain, independente do comportamento do modelo.
Hijack da tag das Trivy Actions — 19 de março de 2026
O aviso da Aqua descreve 76 de 77 tags de versão trivy-action e sete tags setup-trivy redirecionadas para conteúdo malicioso. O entrypoint da action maliciosa recolhia memória dos processos do runner e ficheiros de credenciais; não atribua todo esse percurso de recolha ao binário do scanner. O evento posterior no Docker Hub teve uma janela de exposição separada.
Um workflow que resolvesse uma tag afetada durante o comprometimento poderia executar o payload. As tags são referências mutáveis; por isso, fixe as Actions revistas em SHAs de commits imutáveis e verifique as alterações às dependências. Um coding agent pode propagar a mesma referência insegura para mais ficheiros de workflow.
OpenAI / Hugging Face — avaliações de julho de 2026
O relatório de incidente de 26 de agosto da OpenAI descreve agentes internos de avaliação de cibersegurança a alcançar a Internet através de infraestrutura partilhada, a colaborar através de um quadro de mensagens não autorizado e a comprometer sistemas da Hugging Face. O modelo principal era apenas para uso interno e as avaliações decorreram com salvaguardas reduzidas. Isto constitui evidência sobre esse ambiente de avaliação, não uma taxa de falha medida para agentes disponibilizados publicamente.
A lição de engenharia é que um serviço interno autorizado pode tornar-se num caminho de saída ou num canal de comunicação entre sessões. Teste o que um mirror de packages, um proxy e um armazenamento partilhado podem fazer em nome do agente, e não apenas se a sandbox consegue abrir uma ligação direta à Internet. A investigação independente da METR analisou o comportamento e a colaboração dos agentes; avaliar a eficácia das salvaguardas e a remediação estava fora do seu âmbito.
Intervenção assíncrona do provider
O monitoring de misalignment da OpenAI pode intervir depois de outputs ou ações. Para os modelos abrangidos, os pedidos Responses com reasoning persistido, WebSockets ou compactação da OpenAI podem ser interrompidos automaticamente. Outros pedidos Responses podem gerar alertas sem interrupção automática; o Chat Completions está fora deste sistema. Um webhook não permite bloquear.
Um pedido bloqueado reporta misalignment_policy_violation, com HTTP 403 antes do streaming; os erros também podem surgir a meio do stream. Interrompa as ações seguintes, preserve os registos dos pedidos e das ferramentas e obtenha uma revisão do operador. Não faça retry automaticamente. Os efeitos anteriores permanecem, e os alertas podem conter falsos positivos ou não detetar problemas. Isto acrescenta deteção; não substitui a autorização local.
OWASP ASI Top 10, edição de 2026
A Agentic Security Initiative (ASI) da OWASP é um grupo de trabalho dedicado especificamente a agentes orientados por LLM e, em 9 de dezembro de 2025, publicou o Agentic Security Initiative Top 10 para 2026: um catálogo de dez categorias de risco para a segurança de agentes.
Utilize-o como uma checklist de cobertura para o threat modeling, não como um ranking medido da frequência de incidentes:
Os filtros de conteúdo podem contribuir para detetar instruções maliciosas, incluindo goal hijacking e memory poisoning. As categorias sobrepõem-se: nenhuma pertence exclusivamente a um filtro de texto. Mapeie cada caminho de ataque para os controlos de identidade, políticas de ferramentas, memória, orquestração, monitorização e supply chain, conforme aplicável. O EchoLeak corresponde a ASI01. O Amazon Q corresponde a ASI04 (Supply Chain) e ASI02 (Tool Misuse). O Azure MCP corresponde a ASI03 (Identity). A vulnerabilidade Claude Code CVE-2025-59536 abrange ASI05 (Code Execution), ASI04 e ASI03. O Axios e o Trivy correspondem a ASI04. Este mapeamento mostra por que razão o threat model tem de ir além da entrada e da saída do modelo.
As permissões são infraestrutura, não prompts
É aqui que as guardrails deixam de ser o produto e passam a ser um subsistema de um harness. Três sistemas atuais (OpenAI Agents SDK, Codex CLI e Claude Code) mostram o verdadeiro aspeto de uma superfície de políticas em produção. Os três impõem as permissões no código. Nenhum depende de o modelo agir com cuidado.
OpenAI Agents SDK
O SDK separa o harness do compute. As ferramentas MCP alojadas aceitam require_approval — a string simples "always" / "never" ou um objeto de filtros indexado por essas duas políticas, com os nomes das ferramentas abrangidas por cada uma — e ainda um callback on_approval_request, acionado para cada ferramenta que permaneça sob "always" e que devolve {"approve": bool}, com um motivo opcional. A filtragem granular de ferramentas (tool_filter) está disponível nas variantes de servidor local (MCPServerStdio, MCPServerStreamableHttp, MCPServerSse), caso precise dela:
# No-run: illustrative Agents SDK shape; requires openai-agents, a configured hosted MCP server, and credentials.
from agents import Agent, HostedMCPTool
def approve(request):
# Only tools under the "always" policy reach this callback.
if request.data.name == "delete_repo":
return {"approve": False, "reason": "escalate to a human reviewer"}
return {"approve": True}
agent = Agent(
name="Ops",
tools=[HostedMCPTool(
tool_config={
"type": "mcp",
"server_label": "github",
"server_url": "https://mcp.example.com",
"require_approval": {
"always": {"tool_names": ["delete_repo"]},
"never": {"tool_names": ["list_issues"]},
},
},
on_approval_request=approve,
)],
)
O callback de aprovação é código. A política de aprovação por ferramenta é código. Pode ler este ficheiro. Pode testá-lo. Pode compará-lo com um diff. Nada disso é verdade relativamente a um system prompt que diga «tenha cuidado com a produção».
Codex CLI e a camada de políticas gerida
O coding harness da OpenAI suporta um ficheiro requirements.toml gerido que os departamentos de TI podem distribuir através da gestão de dispositivos. Em sistemas Unix, o ficheiro de sistema encontra-se em /etc/codex/requirements.toml. Funciona como uma camada de restrições rígidas, pelo que as definições ao nível do projeto não podem substituir as respetivas regras:
# /etc/codex/requirements.toml
[rules]
prefix_rules = [
{ pattern = [{ token = "rm" }, { any_of = ["-rf", "-fr"] }], decision = "forbidden", justification = "Recursive force-delete prohibited by IT policy" },
]
prefix_rules.decision aceita apenas "prompt" ou "forbidden", nunca "allow". Um projeto não pode conceder a si próprio uma permissão que a camada gerida proíba. As allowlists de MCP são indexadas pelo nome e pela identidade, por exemplo, uma string de comando ou um URL. Assim, um projeto não pode afirmar que é github-mcp e apontar para o servidor de um atacante. Os requisitos suportados variam consoante o cliente e a versão. A documentação atual exige especificamente o Codex 0.138.0 ou posterior para as chaves de managed permission-profile; por isso, teste qualquer política de requisitos contra todas as versões dos clientes existentes na frota antes da implementação.
Escada de permissões do Claude Code
O Claude Code não publica uma sequência fixa de seis verificações para cada chamada de ferramenta. As regras de permissões são avaliadas deny → ask → allow; a primeira regra correspondente determina o resultado. Um hook PreToolUse é executado antes do pedido de permissão. Um hook pode bloquear uma chamada, mas o resultado de um hook não contorna uma regra correspondente de negação ou de pedido de confirmação. O modo de permissões ativo trata das chamadas que as regras não resolvam. O Claude Agent SDK tem um callback canUseTool separado para pedidos não resolvidos. Esse callback é um mecanismo de controlo do SDK, não uma verificação de permissões da CLI do Claude Code.
Os modos alternam default → acceptEdits → plan com Shift+Tab. auto, bypassPermissions e dontAsk são ativados sob condições de entrada específicas que a camada de políticas gerida pela empresa pode bloquear. Isto é mais do que verificar se um ficheiro de configuração está correto. É uma máquina de estados com regras de precedência, publicada para que uma equipa de segurança possa raciocinar sobre o seu comportamento.
Três raios de impacto num único ficheiro
Eis a estrutura de uma configuração de permissões ao estilo do Codex, com um perfil predefinido e dois perfis nomeados:
# ~/.codex/config.toml
approval_policy = "on-request"
sandbox_mode = "workspace-write"
[profiles.ci]
approval_policy = "never"
sandbox_mode = "read-only"
[profiles.release]
approval_policy = "untrusted"
sandbox_mode = "danger-full-access"
[mcp_servers.github]
command = "gh-mcp"
args = ["--readonly"]
Duas chaves fazem todo o trabalho, e são independentes. approval_policy determina quando é pedida confirmação a uma pessoa. on-request permite ao agente escalar o pedido quando fica bloqueado. never não pergunta nada. untrusted para perante qualquer comando que não esteja na lista de confiança. sandbox_mode determina aquilo em que o comando pode tocar, caso seja executado.
A CI nunca interrompe ninguém e não pode escrever. A release pode aceder à máquina inteira, mas tem de obter primeiro a confirmação de uma pessoa para quase tudo. O perfil de release paga por esse alcance: danger-full-access desativa a sandbox, pelo que a aprovação de untrusted é o único controlo que resta. Tudo o que esteja fora da lista de confiança requer a aprovação de uma pessoa ou não é executado. Essa lista de confiança passa a ser toda a fronteira de segurança.
Os perfis predefinido e CI mantêm o kernel subjacente: Seatbelt no macOS, bubblewrap mais seccomp no Linux e restricted tokens no Windows. Em qualquer dos casos, a opinião do modelo não entra em consideração.
A aplicação da sandbox é uma questão do sistema operativo
É o kernel que faz efetivamente o trabalho aqui. Cada sistema operativo disponibiliza um conjunto de ferramentas diferente, e as duas CLIs nem sempre recorrem ao mesmo componente:
| Plataforma | Claude Code | Codex CLI |
|---|---|---|
| macOS | Seatbelt através de sandbox-exec, com um perfil SBPL (Seatbelt Profile Language) | Seatbelt através de sandbox-exec -p |
| Linux | bubblewrap + proxy de rede socat | bubblewrap + seccomp (Landlock legado através de use_legacy_landlock) |
| Windows | WSL2 obrigatório | restricted tokens nativos + ACLs da área de trabalho + capability SIDs |
Concordam quando o sistema operativo oferece uma opção (Seatbelt, bubblewrap) e divergem quando não oferece. O sandbox do Claude Code requer WSL2 no Windows; isto é uma limitação do sandbox, não uma afirmação de que a CLI não possa ser executada nativamente. O Codex inclui um sandbox nativo para Windows. Em qualquer dos casos, a aplicação das restrições ocorre no kernel, não no modelo.
O caminho Linux do Codex combina três mecanismos de bloqueio ao nível do kernel em torno do comando. PR_SET_NO_NEW_PRIVS impede que o processo obtenha privilégios adicionais, mesmo que tente fazê-lo. Um filtro seccomp faz com que o kernel recuse liminarmente classes inteiras de system calls. As restrições de rede dependem de a rede estar desativada ou de estar configurado um modo proxy; não se limitam universalmente a Unix sockets. Consulte a implementação do sandbox Linux. Um /proc novo e isolado oculta o resto da máquina.
O Codex também reforça o próprio binário no arranque, em todas as plataformas Unix. Define RLIMIT_CORE=0 para suprimir crash dumps e recusa ligações de depuradores. Esta é uma fronteira diferente da do sandbox.
O sandbox do Windows tem dois modos. unelevated usa um restricted token como utilizador e não fornece a aplicação das restrições de rede do modo elevado. elevated usa utilizadores de sandbox dedicados, regras de firewall e ACLs do sistema de ficheiros.
Quando o acesso à rede está desativado, o Codex coloca ficheiros stub .bat e .cmd para ssh e scp num diretório no início de PATH. Esses comandos terminam com um código diferente de zero, em vez de acederem aos binários reais. O Codex também aponta HTTP_PROXY, HTTPS_PROXY, ALL_PROXY e as variáveis de proxy do Git para uma porta local inativa. Estas medidas afetam ferramentas cooperantes; as variáveis de proxy e os command stubs, por si só, não conseguem confinar redes arbitrárias.
Um sandbox também precisa de um modo de falha definido. Na configuração atual do Claude Code, sandbox.failIfUnavailable: true interrompe a execução quando não é possível iniciar o sandbox. allowUnsandboxedCommands: false desativa a via de escape de retry sem sandbox do agente, mas excludedCommands continua a ignorá-la. Teste a configuração efetiva com dependências em falta e um destino proibido.
O credential brokering também está agora disponível localmente. O sandbox.credentials do Claude Code pode recusar ficheiros e variáveis de ambiente nomeados. O masking de variáveis de ambiente, disponível desde a v2.1.199, fornece um placeholder aos comandos em sandbox e permite que o proxy injete o valor real nos pedidos para hosts configurados. Defina injectHosts restritos e configure a terminação TLS; o masking não autoriza a operação solicitada. Estes controlos abrangem comandos Bash executados no sandbox, pelo que hooks, processos MCP e outros caminhos de execução continuam a precisar da sua própria política de credenciais.
Opções de isolamento além do Claude Code e do Codex
Se estiver a criar o seu próprio agente, verá que «sandbox» é um termo abrangente. As opções open source distribuem-se por um espectro — desde wrappers leves de namespaces, num extremo, até microVMs completas, no outro — e a escolha depende do grau de confiança que deposita no código executado no interior.
Isolamento ligeiro — o mesmo kernel, menos privilégios:
- bubblewrap — um construtor de sandboxes de namespaces de baixo nível, utilizado pelo Flatpak e pelo Claude Code no Linux. Quem o invoca tem de escolher o sistema de ficheiros, a rede e a política seccomp opcional; o bubblewrap, por si só, não é uma política de segurança pronta a usar.
- Contentores Docker / OCI padrão — isolamento por namespaces sobre um kernel do anfitrião partilhado. Não são uma sandbox para código não confiável; a própria documentação do gVisor deixa isso claro («containers are not a sandbox»). São um ponto de partida razoável quando combinados com seccomp e AppArmor, e nada mais.
Isolamento ao nível da aplicação e do kernel — o agente comunica com um kernel falso:
- gVisor — o kernel em espaço de utilizador da Google. O contentor pensa que está num sistema Linux, enquanto uma implementação de kernel em Go interceta as chamadas de sistema. Isto reduz a exposição direta ao kernel do anfitrião sem uma VM convidada, com compromissos de compatibilidade e desempenho.
Isolamento por VM completa — um kernel dedicado por sandbox:
- Firecracker — a tecnologia de microVMs da AWS. Cada VM tem o seu próprio kernel Linux sobre KVM; os contentores partilham o kernel do anfitrião. Um comprometimento numa VM ainda tem de atravessar os controlos do VMM ou do anfitrião para afetar o anfitrião ou outra VM, pelo que o Jailer do Firecracker e um anfitrião corrigido continuam a fazer parte da proteção.
- Kata Containers — experiência de utilização de contentores com isolamento ao nível de uma VM. É a opção habitual em clusters Kubernetes que precisam de executar código não confiável.
Plataformas — o que alugaria em vez de construir:
- E2B encapsula o Firecracker numa API de sandbox alojada.
- OpenSandbox separa o SDK do isolamento do runtime configurado pelo administrador. O Docker runc predefinido não é uma microVM; o respetivo caminho Firecracker utiliza Kata com Firecracker através do Kubernetes.
- O Agent Governance Toolkit da Microsoft (com licença MIT, abril de 2026) acrescenta um motor de políticas de runtime. Mapeia os controlos de política para o OWASP ASI Top 10. A alegação relativa à latência de arranque diz respeito a um motor de políticas, não ao custo total de todas as verificações num agente implementado.
Escolha o nível de isolamento com base no nível de confiança do código, no limite entre tenants, no acesso à rede, nos dados do anfitrião e no custo de recuperação. Os controlos de namespaces e seccomp podem ser adequados para ferramentas internas de confiança. Código gerado por LLM e pacotes não confiáveis exigem um limite mais forte, como gVisor, Kata ou uma microVM, seguido de testes aos vetores de escape e exfiltração definidos no seu próprio modelo de ameaças.
O Claude Code e o Codex escolheram do mesmo menu que todos os outros. Apenas o encapsularam de forma diferente.
Hooks PreToolUse como política programável
Os modos e as allowlists resolvem os casos simples: «deixar o agente editar ficheiros, mas não executar bash», «negar tudo o que pareça rm -rf». Falham quando a sua política precisa de lógica real. Quer bloquear git push apenas quando o branch é main. Quer negar qualquer operação Edit que afete um ficheiro cujo nome corresponda a uma expressão regular de segredos. Quer limitar a taxa de chamadas à shell por sessão ou enviar cada invocação de uma ferramenta para o seu log de auditoria central (o SIEM, o sistema de gestão de eventos e informações de segurança que a sua equipa de segurança já monitoriza).
Nada disso cabe numa allowlist estática. É para isso que servem os hooks — comandos shell que o Claude Code executa em pontos específicos do ciclo de vida das tool calls, com capacidade para inspecionar a chamada pendente e devolver uma decisão estruturada de permissão ou recusa. O Claude Code expõe cerca de trinta eventos do ciclo de vida (a lista completa está na documentação), e um deles altera o comportamento de todos os restantes: um hook PreToolUse que devolva permissionDecision: "deny" bloqueia uma tool independentemente do modo.
Eis a estrutura das definições:
{
"permissions": {
"defaultMode": "acceptEdits",
"deny": ["Bash(rm -rf:*)", "Bash(sudo:*)", "Read(.env*)"]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/pre-bash-firewall.sh"
}
]
},
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/protect-paths.sh"
}
]
}
]
}
}
As regras estáticas deny e os hooks dinâmicos têm modos de falha diferentes. Um resultado PreToolUse de deny bloqueia antes do fluxo normal de permissões, mas um command, hook HTTP ou hook de MCP que exceda o tempo limite não bloqueia: o Claude Code prossegue nesse fluxo. Neste exemplo, acceptEdits pode, por isso, aprovar um Edit ou Write quando protect-paths.sh excede o tempo limite. Não faça depender apenas de um command hook uma restrição de caminhos que não possa ser negociada. Coloque as restrições estáticas em regras de recusa ou na sandbox; para políticas dinâmicas, escolha um mecanismo cujo avaliador continue restritivo quando falha e teste tanto o comportamento em caso de timeout como uma recusa explícita.
Um hook pode ser um script shell de cinco linhas ou um motor de políticas completo. O que importa é a estrutura do retorno:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "writes outside workspace prohibited"
}
}
O modelo recebe uma recusa estruturada. O ciclo de raciocínio descrito na Parte 1 trata-a como qualquer outra observação de uma tool: a recusa torna-se contexto, o agente volta a planear e o ciclo continua. É isto que se obtém quando “as permissões são infraestrutura”. A recusa está ligada ao mesmo mecanismo que trata um erro 500 de uma tool HTTP. Não é um fluxo de segurança separado que tenha de ser acrescentado posteriormente.
Um anti-pattern comum consiste em escrever um system prompt que diga “não elimines ficheiros sem confirmação explícita do utilizador”, disponibilizar o agente e depender dessa instrução como controlo. Um prompt injetado, ou um resultado de uma tool controlada por um atacante, pode contornar essa instrução. O modelo não é um motor de políticas. Pode corresponder ao padrão que escreveu ou a um padrão fornecido por um atacante.
A aprovação humana só funciona como escalamento
Os content filters inspecionam o que o modelo diz. As regras de permissões inspecionam a tool call antes de esta ser executada. A revisão human-in-the-loop trata das ações que continuam a exigir uma pessoa. Se as pessoas aprovarem 93% dos pedidos, reveja as regras de escalamento e a qualidade dos pedidos.
O LangGraph fornece a primitiva de pausa/retoma. O HumanLayer encapsula o canal de aprovação, e os dados de utilização da Anthropic mostram por que motivo é necessário medir o número e a qualidade dos escalamentos.
A primitiva do LangGraph
O interrupt() + Command(resume=value) do LangGraph pausa um grafo, persiste o respetivo estado através do checkpointer configurado e retoma a execução com um valor fornecido por uma pessoa. A segurança dessa retoma depende de um detalhe da documentação:
“Quando a execução é retomada (depois de fornecer o input solicitado), o runtime reinicia o nó inteiro desde o início — não retoma a partir da linha exata onde
interruptfoi chamado.”
Deste comportamento de reinício resultam três restrições:
1. Os efeitos secundários anteriores a interrupt() têm de ser idempotentes. Quando o humano responde, o nó é executado novamente desde o início, e não a partir da linha interrupt(). Por isso, se o nó enviar um e-mail, pausar para aprovação e depois devolver "sent", ao retomar o e-mail será enviado uma segunda vez. Solução: coloque os efeitos secundários depois da interrupção ou torne-os seguros para repetir (chaves de deduplicação, upsert em vez de insert, cache por ID da mensagem).
2. As interrupções associam-se às retomas pelo índice, não pelo nome. Se um único nó tiver duas chamadas a interrupt(), o LangGraph associa-as aos valores Command(resume=...) pela ordem em que são executadas. Qualquer ramificação que altere o número de interrupções executadas (um if que ignore uma delas ao retomar, ou um ciclo que seja iterado um número diferente de vezes) desalinha os índices, pelo que um valor de retoma pode ser aplicado à interrupção errada.
3. Mantenha os payloads seguros para JSON. A documentação do LangGraph exige valores serializáveis em JSON para interrupt() e para os payloads de retoma. Use strings, números, booleanos, arrays e dicionários que contenham esses valores. Evite funções, instâncias de classes e outros objetos complexos, porque a serialização depende do checkpointer configurado. Converta os dados de aprovação em dicionários e valores primitivos antes de os passar a interrupt() ou de os expor através de uma API HTTP.
Os três padrões canónicos:
# No-run: illustrative LangGraph sketches; requires LangGraph, a tool decorator, interrupt, and smtp_send.
# (a) Approval check
@tool
def send_email(to, subject, body):
resp = interrupt({"action": "send_email", "to": to,
"subject": subject, "body": body})
if resp.get("action") == "approve":
return smtp_send(to, subject, body)
return "Email cancelled"
# (b) Edit-and-continue
def review_node(state):
edited = interrupt({"content": state["generated_text"]})
return {"generated_text": edited}
# (c) Mid-run state correction — conditional edge
class AgeState(TypedDict):
age: int | None
pending_question: str | None
def get_age_node(state: AgeState):
question = state.get("pending_question") or "What is your age?"
answer = interrupt(question) # once per node invocation
if isinstance(answer, int) and answer > 0:
return {"age": answer, "pending_question": None}
return {"pending_question": f"'{answer}' is not valid. Please enter a positive number."}
def route_age(state: AgeState):
return END if state.get("age") is not None else "get_age"
builder = StateGraph(AgeState)
builder.add_node("get_age", get_age_node)
builder.add_edge(START, "get_age")
builder.add_conditional_edges("get_age", route_age)
A retoma é feita com graph.invoke(Command(resume={"action": "approve"}), config=cfg). O LangGraph 0.4+ suporta retomas baseadas em dicionários para várias interrupções em ramos paralelos, o que se torna relevante assim que o seu agente começa a distribuir trabalho por vários ramos.
HumanLayer: aprovação como produto
HumanLayer é a versão gerida da mesma ideia. Decore uma função e os pedidos de aprovação serão encaminhados para o Slack, e-mail ou Discord, com regras que determinam quem deve ser notificado. Quando o agente tenta chamar multiply(2, 5), os registos têm este aspeto:
last message led to 1 tool calls: [('multiply', '{"x":2,"y":5}')]
HumanLayer: waiting for approval for multiply
O aprovador clica em aprovar ou rejeitar no Slack. Em caso de rejeição, a documentação do HumanLayer descreve-o assim: “O HumanLayer transmitirá o seu feedback ao agente, que poderá então ajustar a sua abordagem.” Esse feedback permite ao agente rever o seu plano em vez de tratar a rejeição como um beco sem saída.
Fadiga de aprovações nos dados
A Anthropic publicou os dados reais em fevereiro de 2026. Há três conclusões mais importantes do que as restantes.
“Descobrimos que 80% das chamadas a ferramentas são feitas por agentes que parecem ter pelo menos um tipo de salvaguarda (como permissões restringidas ou requisitos de aprovação humana), 73% parecem envolver um humano de alguma forma e apenas 0,8% das ações parecem ser irreversíveis.”
Essa é a boa notícia. Trate os 80% como um limite superior, porque a nota de rodapé 14 da Anthropic acrescenta que “o Claude sobrestimava frequentemente o envolvimento humano, pelo que esperamos que 80% seja um limite superior.”
“Os utilizadores mais recentes (<50 sessões) usam a aprovação automática total aproximadamente 20% das vezes; ao fim de 750 sessões, este valor aumenta para mais de 40% das sessões.”
Esta é a tendência. Os utilizadores começam com cautela e tornam-se menos cautelosos à medida que desenvolvem confiança na ferramenta. É isso que os seres humanos fazem, e não é um defeito de carácter. É um sinal de telemetria que o seu sistema deve acompanhar. (Uma pequena nota de verificação factual: a cobertura secundária citou amplamente este dado como “20% → mais de 50%”. Face aos dados primários da Anthropic, o valor verificado é 20% → mais de 40%. Se já viu o valor de 50%, é daí que ele veio.)
O artigo de engenharia da Anthropic sobre o modo automático do Claude Code, publicado em março de 2026, fornece o número essencial:
“Os utilizadores do Claude Code aprovam 93% dos pedidos de permissão. Criámos classificadores para automatizar algumas decisões, aumentando a segurança e reduzindo a fadiga causada pelas aprovações… Se uma sessão acumular 3 recusas consecutivas ou 20 recusas no total, paramos o modelo e escalamos para um humano.”
Uma taxa de aprovação elevada justifica analisar se os prompts ajudam as pessoas a decidir; não prova um limiar universal de falha. O classificador em duas fases da Anthropic reduz o número de prompts e escala recusas agrupadas. A taxa de falsos positivos de 0,4% comunicada pela empresa usou 10 000 exemplos de tráfego. As taxas de falsos negativos foram de 17% em 52 ações reais excessivamente permissivas e de 5,7% em 1 000 casos sintéticos de exfiltração. Estas são populações diferentes, não percentagens complementares nem uma prova de que a classificação substitui o sandboxing.
Medir a qualidade do escalamento
Coloque na allowlist as ações rotineiras e reversíveis e registe-as. Escale as ações cujos efeitos secundários atravessam um limite que o runtime não consegue desfazer, como enviar uma mensagem externa, escrever em produção, fazer um force push ou efetuar um pagamento. A Anthropic apresenta o objetivo como manter um humano capaz de intervir quando a decisão tem consequências reais.
Acompanhe o funil completo em vez de procurar uma taxa de aprovação de referência obtida de terceiros: ações propostas, autorizações automáticas, escalamentos, aprovações, recusas, edições e incidentes após a aprovação. Uma taxa de aprovação elevada pode significar que os prompts são apenas ruído rotineiro. Uma taxa elevada de recusas ou edições pode significar que o planner está a propor a ação errada ou a ocultar a informação de que o aprovador precisa. O limiar adequado depende da classe de ação e do custo de uma autorização indevida; por isso, defina-o com base nos seus próprios dados de incidentes e revisões.
Scoping do MCP e a cadeia de fornecimento
O MCP liga agentes a ferramentas externas, como Slack, GitHub e bases de dados, o que faz do seu modelo de autorização parte da fronteira de segurança. As revisões da especificação de 2025 separaram os papéis de emissor de tokens e servidor de recursos e acrescentaram resource indicators. Esse histórico explica que verificações de audience e de forwarding um servidor tem de aplicar atualmente.
A autorização MCP em três revisões
A autorização era opcional nas implementações MCP da especificação de 2025-03-26. Para uma implementação HTTP em produção que proteja dados ou ferramentas de utilizadores, recomendo OAuth 2.1 com PKCE (Proof Key for Code Exchange), que a especificação exige quando uma implementação suporta autorização OAuth. O desenho inicial permitia que um servidor MCP desempenhasse dois papéis. O servidor de autorização emite tokens; o servidor de recursos aceita-os. São papéis distintos, mesmo quando um único serviço desempenha ambos. Se esse serviço reencaminhar um pedido para outro servidor, a mesma credencial pode chegar a um destino para o qual nunca foi concebida. Essa é a vulnerabilidade.
A revisão de 2025-06-18 tornou os papéis explícitos. Um servidor MCP protegido atua como servidor de recursos OAuth, enquanto um servidor de autorização emite o token. O servidor de autorização pode ser alojado juntamente com o servidor de recursos ou executado separadamente. Os Resource Indicators do RFC 8707 associam o token a um recurso-alvo, e os Protected Resource Metadata do RFC 9728 fornecem ao cliente um caminho explícito de descoberta. A especificação também proíbe um servidor MCP de reencaminhar o token do cliente para montante.
A revisão de 2025-11-25 manteve essa separação e trabalhou os aspetos que um cliente tem de implementar corretamente. A descoberta do authorization server passou a incluir o OpenID Connect Discovery, permitindo ao cliente encontrar o issuer correto em vez de o adivinhar. O consentimento incremental para scopes passou para o cabeçalho WWW-Authenticate, permitindo ao servidor pedir mais um scope no momento em que precisa dele, em vez de exigir tudo à partida. O registo de clientes passou a incluir OAuth Client ID Metadata Documents como mecanismo recomendado, substituindo o dynamic registration na maioria das implementações. A descoberta de Protected Resource Metadata também foi alinhada com a RFC 9728, tornando WWW-Authenticate opcional, com .well-known como alternativa.
Consulte a página de versionamento antes de implementar. A 6 de setembro de 2026, a revisão atual é 2026-07-28. Esta exige que cada pedido declare a versão do protocolo e permite ao servidor aceitar ou rejeitar cada pedido de forma independente. Um cliente pode chamar server/discover para selecionar antecipadamente uma versão, mas a descoberta é opcional. A declaração e a negociação por pedido continuam a ser obrigatórias, incluindo quando o cliente trata um erro de versão não suportada e repete o pedido com uma versão mutuamente suportada.
A vinculação ao audience limita os ataques de replay contra o servidor MCP errado. Não neutraliza as vulnerabilidades distintas de configuração do Claude Code descritas acima: um hook no lado do host pode continuar a ser executado antes de o modelo arrancar, e um projeto não fiável pode continuar a tentar alterar a configuração local. O scope do token, a confiança no projeto, a política de hooks e o sandboxing continuam a ser controlos independentes.
A checklist MCP de 2026
Se disponibiliza ou consome MCP em produção:
- Trate a autenticação como um requisito de produção, não como uma predefinição do protocolo. O MCP deixa a autorização como opcional, mas recomendo OAuth 2.1 com PKCE para uma implementação HTTP protegida. O aviso sobre o serviço MCP do Azure Web Apps dizia respeito à ausência de autenticação. Se o seu servidor aceitar tráfego sem verificar as credenciais do chamador, criou uma ferramenta que qualquer pessoa com acesso a ela pode chamar.
- Os tokens estão vinculados ao audience. Peça um token para o recurso MCP de destino e valide que o token apresentado identifica o seu servidor como audience. Rejeite tokens emitidos para outro recurso.
- Isole deliberadamente as autoridades de leitura e escrita. O MCP vincula um token a um resource server, não a uma ferramenta individual. Se um servidor Slack aceitar uma credencial com
chat:writee a encaminhar para handlers de leitura e escrita, uma ferramenta orientada para leitura pode tornar-se num caminho de envio de mensagens através da política desse servidor. Use resource servers separados ou credenciais e verificações de autorização separadas quando as operações de leitura e escrita precisarem de blast radii independentes. - Use tokens recentes e de curta duração em vez de API keys permanentes. O padrão do vault do Claude Managed Agents (engenharia da Anthropic) é a referência: o próprio agente nunca vê as credenciais reais. O proxy obtém as credenciais armazenadas correspondentes do vault, chama a ferramenta em nome do agente e devolve o resultado. Emitir um token novo para cada chamada não é uma garantia documentada; credenciais de curta duração são uma recomendação de implementação.
Os controlos da cadeia de fornecimento continuam a aplicar-se
Os incidentes do axios e do Trivy são falhas conhecidas da cadeia de fornecimento de pacotes e de CI, aplicadas a sistemas que automatizam a instalação de dependências. A automatização aumenta o número e a velocidade das execuções, pelo que os controlos de versão, proveniência e revisão têm de ser executados antes de o comando gerado chegar à CI ou a uma sandbox.
A defesa é simples:
- Fixe as versões no lockfile. Os agentes nunca devem resolver uma versão flutuante — nem
@latest, nemnpm update, nem--upgrade. - Faça scanning na CI com ferramentas independentes do componente que está a ser verificado.
- Use SHA de commits do GitHub para Actions, não tags.
- Reveja os diffs de dependências nos PRs gerados por agentes antes do merge.
Estes são controlos padrão da cadeia de fornecimento. A automatização por agentes altera a frequência, não o mecanismo.
Uma stack de políticas para o Market Analyst Agent
O Market Analyst Agent da Parte 1 é um agente LangGraph pequeno que obtém dados de mercado e escreve um relatório de analista — mas não é tão pequeno como essa descrição sugere. Para além das ferramentas de dados de mercado, executa uma CLI com allowlist através de subprocess, avalia Python escrito pelo modelo dentro do processo e cria registos de transações simuladas. Exercita invocação de ferramentas, execução de código e encaminhamento para aprovação, mas não coloca ordens reais. Eis o aspeto de uma stack de políticas mínima para este agente.
Camada 1: um hook PreToolUse que bloqueia antes da execução
Até um agente que «apenas lê dados de ações» pode tentar aceder a coisas que não deve: um curl para um URL controlado por um atacante, escritas fora da workspace, mutações git no repositório do host. Uma regra de bloqueio é infraestrutura, não um prompt. O exemplo abaixo devolve a própria estrutura de decisão do agente, não o envelope hookSpecificOutput que o Claude Code espera.
# agent/permissions.py
from pathlib import Path
DENY_COMMANDS = frozenset({
"rm -rf", "sudo", "chmod 777",
"curl -X POST", "wget", "nc ",
})
WORKSPACE = Path("./workspace").resolve()
def _outside_workspace(path: str) -> bool:
# Resolve first: "~/.ssh/id_rsa" and "workspace/../../etc" both
# have to become real paths before the comparison means anything.
return not Path(path).expanduser().resolve().is_relative_to(WORKSPACE)
def pre_tool_use(tool_name: str, args: dict) -> dict | None:
if tool_name == "shell":
cmd = args.get("command", "")
if any(bad in cmd for bad in DENY_COMMANDS):
return {"permissionDecision": "deny",
"reason": f"command pattern disallowed: {cmd!r}"}
if tool_name == "write_file":
path = args.get("path", "")
if _outside_workspace(path):
return {"permissionDecision": "deny",
"reason": f"path outside workspace: {path!r}"}
return None # fall through to mode / canUseTool
O exemplo torna visível o ponto de controlo. O hook devolve um bloqueio estruturado, e o ciclo de raciocínio recebe essa recusa como uma observação da ferramenta.
A verificação do caminho é uma allowlist: uma única raiz da workspace; tudo o resto é bloqueado. Uma deny-list de prefixos proibidos só bloqueia os caminhos em que pensou. ~/.ssh/id_rsa nunca é escrito da forma que registou. A verificação de comandos continua a ser uma deny-list. A correspondência por substring não é uma política de shell adequada para produção. Uma implementação real deve analisar o comando e depender da sandbox do sistema operativo quando este chegar à execução. O exemplo, por si só, não constitui uma fronteira de execução: se um hook externo o executar, um timeout tem de deixar em vigor uma restrição independente sobre a workspace.
Camada 2: um canário de entrada para prompt injection
O sequestro do objetivo do agente (ASI01) chega frequentemente através de uma página Web obtida, de uma mensagem do utilizador ou de um PDF de um artigo de investigação. Um canário regex simples deteta padrões literais de instruções e cria um evento de telemetria útil. Não deteta injeções ofuscadas, multilingues ou dependentes do contexto, pelo que não pode servir como fronteira de decisão:
# agent/input_canary.py
import re
INJECTION_PATTERNS = [
re.compile(r"ignore\s+(?:all\s+|any\s+|the\s+)?"
r"(?:previous\s+|prior\s+|above\s+|earlier\s+)?"
r"(?:instructions|rules|prompts?)",
re.IGNORECASE),
re.compile(r"you are now|act as|roleplay as", re.IGNORECASE),
re.compile(r"system[ _:]*prompt", re.IGNORECASE),
re.compile(r"<\|im_(start|end)\|>"),
]
def input_canary(text: str) -> dict | None:
for pat in INJECTION_PATTERNS:
m = pat.search(text)
if m:
return {"flag": "possible_injection", "match": m.group(0)}
return None
Registe as entradas sinalizadas; não as rejeite automaticamente. Os falsos positivos são dispendiosos neste contexto para um assistente de investigação. Mas é o log que permite perceber quando o número de sinalizações aumenta subitamente devido a um utilizador.
Camada 3: validação de saída estruturada através de um stop hook
Um modelo Pydantic e um hook Stop proporcionam um ciclo rigoroso de validação e nova tentativa para gerar relatórios. O agente não pode declarar “concluído” enquanto a saída não passar pela validação do schema e por um smoke test:
# No-run: illustrative policy sketch; requires Pydantic and the repo-local agent.schemas module.
# agent/stop_hook.py
from pydantic import ValidationError
from agent.schemas import MarketReport
def on_stop(final_output: str) -> dict:
try:
report = MarketReport.model_validate_json(final_output)
except ValidationError as e:
return {"decision": "continue",
"feedback": f"schema invalid: {e.errors()[:3]}"}
if not report.tickers:
return {"decision": "continue",
"feedback": "no tickers in report — did you skip the snapshot step?"}
return {"decision": "allow_stop"}
Uma verificação do schema e um único smoke test fazem a diferença entre “o agente disse que estava concluído” e “a saída é efetivamente um relatório”.
Camada 4: aprovação antes de ações de saída
O execute_trade do analista de mercado é uma transição de estado simulada e idempotente, pelo que demonstra o encaminhamento para aprovação, e não um efeito financeiro irreversível. Numa integração real de saída — email, Slack, um relatório para um cliente ou uma ordem de corretagem — mostre à pessoa a ação proposta e aguarde aprovação ou rejeição antes de executar a ferramenta. Use interrupt() para essa pausa:
# No-run: illustrative outbound-tool sketch; requires LangGraph, a tool decorator, and smtp_send.
# agent/tools/notify.py
from langgraph.types import interrupt
@tool
def send_report(to: str, body: str):
resp = interrupt({
"action": "send_report",
"to": to,
"body": body, # Review the complete content that will execute.
})
if resp.get("action") == "approve":
return smtp_send(to, body)
return "send cancelled by human"
As ações de saída completam a tríade letal. Exija aprovação para ações de saída quando a autorização existente e a política de deployment não as abrangerem. Associe a aprovação ao destino e ao conteúdo completos; argumentos alterados exigem uma nova decisão. As mensagens para o departamento financeiro, clientes ou outros destinatários externos devem mostrar ao aprovador o que será enviado e para onde.
O que esta stack não faz
Isto não constitui uma defesa contra:
- Uma dependência upstream comprometida (da classe axios). O agente executa o que
uv synclhe indicar para executar. - Um
.mcp.jsonmalicioso num repositório clonado (da classe CVE-2025-59536). É no modelo de permissões do cliente MCP do host que isso é detetado, não no código do agente. - Uma cadeia de roubo de dados construída com ferramentas legítimas (da classe EchoLeak) — o agente lê dados privados, obtém URLs externas e envia mensagens. Interrompa ou restrinja esse caminho de exfiltração com acesso a dados com âmbito limitado, routing de confiança, restrições de egress e aprovação quando necessário. Remover uma capacidade bloqueia este caminho específico, não todos os ataques possíveis.
- Uma fuga de
execute_python_analysis, o avaliador Python em processo do agente. Bloqueia uma lista de tipos de instruções, rejeita qualquer identificador que comece por um underscore e permite importações apenas dejson,mathestatistics. No entanto,execno processo worker não é uma fronteira: um bypass executa com os file handles e o acesso à rede do worker. Coloque-o atrás de isolamento do sistema de ficheiros, da rede e das credenciais, além de limites de CPU, memória e tempo, antes de avaliar código não confiável. Um subprocesso, por si só, herda o acesso e não é um sandbox de segurança.
Estas quatro camadas são políticas locais, e a política local é a camada mais interna que controla, não a única. Cada item dessa lista tem de ser intercetado noutro ponto — no lockfile, no cliente MCP, na fronteira do processo que envolve o código gerado ou em controlos que impeçam instruções não confiáveis de ligar dados privados a um destino de exfiltração.
Principais conclusões
- Os filtros de conteúdo e a política de execução protegem fronteiras diferentes. Os filtros inspecionam a entrada e a saída do modelo. A autorização de ferramentas, o âmbito das credenciais, as sandboxes e os controlos da cadeia de fornecimento atuam nos percursos utilizados nos sete incidentes.
- A maioria das categorias ASI da OWASP requer controlos fora da saída do modelo. Use a lista para associar cada ameaça ao componente que a pode efetivamente bloquear ou registar.
- A permissão é infraestrutura, não um prompt. O Claude Code documenta a precedência das regras deny, ask e allow, enquanto
PreToolUsepode bloquear antes da execução. O Claude Agent SDK expõe um percursocanUseToolseparado. Outros runtimes precisam de um modelo de precedência igualmente testável. - Trate uma rejeição estruturada de um hook PreToolUse como mais uma observação de ferramenta. O ciclo de raciocínio já a processa. Não precisa de um fluxo de segurança separado.
- Uma taxa de aprovação de 93% é um sinal para inspecionar a qualidade dos prompts e a frequência das escalações. Registe edições, rejeições e incidentes após a aprovação, em vez de copiar um objetivo universal.
- Os tokens associados ao público-alvo e os cofres por sessão limitam a reutilização e a exposição de credenciais. Não substituem a confiança no projeto, a política dos hooks nem a sandboxing.
- As verificações da cadeia de fornecimento têm de acompanhar a velocidade da automação. Fixe versões e os SHAs das Actions, faça análises em CI e reveja as alterações às dependências em pull requests criados por agentes.
- Crie a camada de política de modo que o lançamento de um novo produto não a invalide. O OpenAI Agents SDK, o Codex CLI e o Claude Code expressam as mesmas primitivas de formas diferentes. São as primitivas — escadas de permissões, hooks, sandboxes, interrupções e tokens associados ao público-alvo — que estão realmente em causa.
A camada seguinte é o runtime
A Parte 5, Long-Running AI Agent Runtime, mostra onde ficam a sandbox, o broker de segredos, o checkpoint e o registo de auditoria durante uma execução longa. A Parte 6 entra depois no harness, onde esta escada de permissões é uma de várias etapas, e analisa como as verificações de aceitação, as tentativas repetidas e a avaliação orientada por traces impedem que o ciclo declare sucesso demasiado cedo. Acrescenta também uma questão de que este artigo não precisava: se é sequer seguro reenviar uma chamada que sofreu timeout a meio da execução.
Referências
As perspetivas
- Bharani Subramaniam e Martin Fowler, Emerging Patterns in Building GenAI Products.
- Simon Willison, The lethal trifecta for AI agents, 16 de junho de 2025.
- Joel Fokou, Parallax: Why AI Agents That Think Must Never Act, arXiv 2604.12986, 14 de abril de 2026 (não revisto por pares).
- Alessandro Pignati, Your AI Agent Has Too Much Power: Understanding and Taming Excessive Agency, janeiro de 2026.
Produtos de guardrails para LLMs
- NVIDIA NeMo Guardrails
- Meta Llama Guard 4
- Guardrails AI
- Lakera Guard
- AWS Bedrock Guardrails
- Azure Content Safety: Prompt Shields
- openai-guardrails-python
Incidentes
- Itay Ravia (anteriormente na Aim Labs, agora na Cato Networks), Análise detalhada do EchoLeak (CVE-2025-32711).
- AWS, aviso do Amazon Q Developer VS Code v1.84.0 (CVE-2025-8217).
- Microsoft, registo CVE do Azure MCP Server (CVE-2026-32211; referência do fornecedor: Microsoft).
- Check Point Research, execução remota de código e exfiltração de tokens de API através de ficheiros de projeto do Claude Code (CVE-2025-59536).
- axios, análise pós-incidente do comprometimento das versões v1.14.1 / v0.30.4.
- Aqua Security, sequestro da tag das Trivy Actions (GHSA-69fq-xp46-6x23).
Superfícies de política
- OpenAI Agents SDK — documentação das ferramentas MCP
- Configuração gerida do Codex CLI
- Modos de permissões do Claude Code
- Sandboxing do Claude Code
- Agentes geridos da Claude
HITL
- Documentação das interrupções do LangGraph
- Início rápido do HumanLayer para Python
- Anthropic, Medir a autonomia dos agentes de IA na prática, 18 de fevereiro de 2026.
- Anthropic, modo automático do Claude Code, 25 de março de 2026.
- Jackson Wells (Galileo), Como criar supervisão human-in-the-loop para agentes de IA em produção, 21 de dezembro de 2025.
OWASP
- OWASP Agentic Security Initiative, Top 10 para aplicações agentic, 2026, 9 de dezembro de 2025.
A camada de políticas do Market Analyst Agent reside no grafo combinado de análise para negociação existente no repositório, e não no grafo de análise indicado na Parte 1. Governa o estado de negociação simulado: um nó guardião determinístico rejeita ações restritas, aprova automaticamente as de baixo valor e encaminha as restantes para um nó de responsável pela conformidade, antes de o grafo terminar com interrupt_before. A camada de políticas está no GitHub. O deny hook, o input canary e o validador do Stop-hook acima são esboços dos mesmos pontos de controlo. Foram escritos para serem lidos, não para serem simplesmente colocados nesse repositório.