Conclusão primeiro: em 2026, segurança de AI Agent não é «o modelo vai dizer a frase errada?». É «dados estruturados não confiáveis podem virar uma invocação real de ferramenta?». Um chatbot injetado, no pior caso, emite uma resposta enganosa. Um Agent injetado, no pior caso, envia e-mail, edita arquivos, consulta um banco ou dispara um pagamento. JSON é ao mesmo tempo o contrato e a carga. Prompt Injection muitas vezes não mora na caixa de chat — mora em campos de ticket, páginas web, corpos de e-mail e respostas de API, todos chegando como JSON.
Escrito em 14 de setembro de 2026. Já cobrimos por que Tool Calling depende de JSON Schema, o fluxo de dados JSON do Agent, JSON Schema como contrato e MCP / Skills / Tools / Subagents. Este texto só responde o que arriscam o JSON malicioso, a injeção indireta, o Tool Calling com privilégio demais e os Schema frouxos — e o que o Host deve impor. É um guia defensivo. Não oferece passos de ataque reproduzíveis nem texto de injeção pronto para usar.
Uma tabela: superfície de ataque de chatbot vs Agent
O que o usuário digita é só uma fatia do que um Agent ingere. Na pilha padrão de 2026 o modelo também lê resultados de tools, Resources de MCP, trechos de página, campos de ticket e corpos de e-mail — quase sempre como JSON, ou como strings embrulhadas em JSON. As listas do setor costumam meter isso em Prompt Injection, Insecure Output Handling e Excessive Agency. Você não precisa da taxonomia primeiro. Precisa saber quem está falando e quem está executando.
| Dimensão | Chatbot | Agent que pode chamar tools |
|---|---|---|
| Modo de falha | Uma resposta errada ou induzida | Um efeito colateral real: escrever, requisitar, mutar dados |
| Entrada não confiável | A mensagem atual do usuário | Texto do usuário + JSON externo + resultados de tools + campos de página/e-mail |
| Executor | O modelo só emite texto | Host / MCP Server executa arguments |
| Contrato | O prompt (mole) | JSON Schema + validação do Host (duro) |
| Ajuste mínimo | Ajustes de prompt, política de recusa | Schema mais apertado, allowlist de tools, autorização independente |
Uma frase: o modelo pode ser enganado; o Host não deve executar junto. O limite de segurança fica entre gerar tool_calls e de fato rodá-los. Validação, autorização e auditoria pertencem ali — não «tratar a saída do modelo como um RPC confiável».
JSON malicioso: campos a mais, confusão de tipos, payloads aninhados
JSON malicioso costuma estar bem formado e ainda assim ser perigoso. Um parser estrito rejeita vírgula final ou comentários. Um parser frouxo, concatenação de strings ou «trate como texto e depois JSON.parse» só atrasam a falha. Em sistemas Agent o estrago começa quando o Host já trata o valor como objeto.
Três formas para defender primeiro (só padrões — não um how-to):
| Padrão | O que parece | Se o Host não valida | Defesa |
|---|---|---|---|
| Campos a mais | Um objeto de negócio com chaves não declaradas | Vira merge na config, ou segue para o próximo tool | additionalProperties: false; descartar chaves desconhecidas |
| Confusão de tipos | Um number/array chega como string ou object | Ramos de autorização erram, ou um blob inteiro vira argumento | Fixar type, enum, format |
| Payload aninhado | Um campo string que embute mais JSON ou um brief longo | O texto interno entra no prompt e é lido como instruções | Limites de comprimento; validar o JSON interno; nunca tratar texto cru como fala de system |
Um contrato de parâmetros de tool que «funciona» porque quase não restringe nada, e depois a versão apertada. São amostras defensivas, não material de ataque:
{
"type": "object",
"properties": {
"payload": { "type": "object" }
}
}
Esse Schema quase não é contrato: qualquer chave, qualquer aninhamento passa. Em produção, allowlist de campos e extras proibidos:
{
"type": "object",
"properties": {
"order_id": { "type": "string", "pattern": "^A-[0-9]{4,8}$" },
"amount": { "type": "number", "minimum": 0, "maximum": 100000 },
"note": { "type": "string", "maxLength": 200 }
},
"required": ["order_id", "amount"],
"additionalProperties": false
}
Ao revisar amostras, use o validador JSON deste site contra esse Schema, e o JSON Diff para comparar «arguments que o modelo emitiu» com «o objeto mínimo que você permite». Chaves a mais e tipos invertidos são o primeiro alarme.
Outro esquecimento: não faça merge de objetos não confiáveis na config interna nem em estruturas que tocam a cadeia de protótipos. Num Host JavaScript, chaves desconhecidas num objeto de config podem significar mais do que «um campo extra». O conserto é o mesmo: projete um objeto de allowlist a partir do Schema e passe esse adiante.
Prompt Injection: instruções escondidas em dados estruturados
Injeção direta é o usuário falando na caixa de chat, tentando sobrescrever o system prompt. Injeção indireta combina com o jeito que Agents de 2026 de fato rodam: as instruções se escondem em dados que o modelo vai ler depois — títulos de ticket, corpos de e-mail, parágrafos de página, trechos de PDF, strings JSON devolvidas por outro tool. Modelos não separam naturalmente «regras que o desenvolvedor escreveu» de «frases dentro dos dados».
Dados estruturados deixam isso mais silencioso. Uma nota de cliente é só customer_note numa API. Depois de entrar no contexto, ela divide o fluxo de tokens com o system prompt. Se o Host concatena resultados de tools no turno seguinte do jeito que vieram, um site externo ou um remetente está, na prática, escrevendo prompts para o Agent.
Defesa não é mais uma frase do tipo «por favor ignore instruções maliciosas». Prompts reduzem risco; não são fronteira. Controles mais estáveis:
- Rotule a procedência — texto externo entra com um papel ou wrapper distinto (por exemplo
tool/untrusted), nunca dobrado no system. - Encolha a superfície visível — resuma em vez de colar; mande só os campos que você precisa, não o documento JSON inteiro.
- Ponha portão em ações de alto impacto — transferências, exclusões, envio de saída precisam de um motor de política ou de um humano, independentes do modelo.
- Trate resultados de tools como dados, não como instruções — valide os retornos com Schema antes de eles voltarem ao prompt.
O mesmo instinto de como o Apple Intelligence protege dados pessoais: o que vaza ou é abusado costuma ser um campo que você colocou no JSON. No modelo, um campo pode ser lido como fala; nos arguments de um tool, como uma ação.
Tool Calling: abuso de privilégio que parece uma chamada válida
O perigo não é «o modelo consegue emitir JSON?». É se o Host trata o modelo como um chamador já autorizado. É o clássico confused deputy: o modelo propõe; a permissão pertence à sessão do usuário, ao tenant e à conta de serviço. «Chame export_orders» não é a mesma coisa que «este usuário pode exportar a tabela».
Excesso de privilégio em 2026 costuma parecer isto:
- Um único
run_command/http_requestcom strings quase arbitrárias; - Tools que rodam com conta de serviço mais ampla que o usuário final;
- O Host checa a sintaxe JSON, mas não «este usuário pode mexer neste recurso?»;
- JSON do usuário ou da página passa direto como arguments do tool, pulando o seu Schema.
Uma declaração aberta demais, e depois uma que você de fato consegue auditar:
{
"name": "fetch_url",
"description": "Fetch any URL and return text.",
"parameters": {
"type": "object",
"properties": {
"url": { "type": "string" }
},
"required": ["url"]
}
}
{
"name": "fetch_public_doc",
"description": "Fetch an allowlisted documentation URL.",
"parameters": {
"type": "object",
"properties": {
"doc_id": {
"type": "string",
"pattern": "^doc-[a-z0-9-]{1,32}$"
}
},
"required": ["doc_id"],
"additionalProperties": false
}
}
A segunda não é «mais inteligente». Ela vira uma capacidade aberta num identificador auditável. O Host resolve doc_id contra a própria allowlist, completa a origem e aplica timeouts e tetos de tamanho. O modelo nunca vê uma URL arbitrária, então sobra um caminho a menos até uma fonte não confiável.
Antes de executar, três perguntas: este tool está na allowlist da sessão? Os arguments passaram um Schema e uma checagem de autorização independentes do modelo? Na falha, você recusa — ou devolve o detalhe do erro como texto fresco injetável? Erros de parâmetros e validação cobre o pipeline. Em segurança, acrescente: falhe fechado, e não deixe o modelo retentar com arguments novos num loop sem teto.
Um Schema frouxo demais é uma vulnerabilidade
Structured Output e Tool Calling usam os dois JSON Schema para bloquear tokens ilegais — o contrato padrão de 2026, veja o que é Structured Output. Schema garante forma, não sentido seguro.
Contratos frouxos costumam parecer isto:
type: objectsemproperties, ouadditionalPropertiesdeixado em true;- Um nome de ação que deveria ser enum, escrito como qualquer
string; - Um campo identificador que cabe um brief de uma página;
- O modo
strictdo fornecedor cobre um subconjunto, e o Host assume que «o lado do modelo já bloqueou tudo».
Empilhe duas portas: o Schema do lado do modelo reduz disparates; o Host valida de novo com o mesmo Schema (ou um mais estrito) e projeta para um tipo interno. Structured Outputs do fornecedor não substituem a sua autorização. Diferenças de campo entre fornecedores já são risco — veja a comparação de Structured Output. Uma migração que afrouxa o contrato alarga a superfície de ataque.
Quando Schema é controle de segurança, prefira enum, const, pattern, maxLength, minimum / maximum, required, additionalProperties: false. Se você precisa de texto livre, isole, coloque teto e nunca mapeie texto livre para nome de tool nem para URL.
MCP e ferramentas externas: onde fica o limite de confiança
MCP resolve descoberta e invocação entre processos. Não resolve «este Server é benigno?». O que é MCP já traça a linha JSON-RPC: API do modelo de um lado, Host ↔ Server do outro. Segurança precisa de uma segunda linha: descrições de tools, texto de Resources e JSON de retorno de um MCP Server de terceiros são entrada não confiável.
O risco não é a data do protocolo. É confiança colocada na camada errada:
description/inputSchemadetools/listcolados no system prompt;resultdetools/callentrando no turno seguinte sem validação;- Servidores demais num Host, nomes ou capacidades sobrepostos, e executa mesmo assim;
- Tratar «o usuário permitiu este Server» como «cada frase que ele devolve é uma instrução».
Skills têm o mesmo tipo de problema: um Skill é um how-to, não uma camada de autorização. Carregar SKILL.md de um repositório não confiável acrescenta um fluxo que o modelo vai seguir. Veja o que as quatro camadas controlam — não troque Schema por um Skill, nem isolamento de permissão por um Subagent. Um Subagent pode estreitar tools; o pai ainda decide quais segredos ele recebe.
Lista de defesa: validar, allowlist, menor privilégio
Aperte ao longo do caminho de execução, de fora para dentro — não comece pelo prompt:
- Escreva o contrato primeiro. Um JSON Schema por tool:
requiredcompleto,additionalProperties: false, identificadores viapattern/enum. - Valide de novo no Host. Não confie em «o modelo já seguiu o Schema». Use ajv ou equivalente; falhe fechado.
- Projete; não repasse. Copie campos da allowlist para um DTO interno e só então chame a API de negócio.
- Minimize tools. Prefira
fetch_public_docafetch_url; prefira só leitura a escrita. - Autorize como o usuário, não como o modelo. Cheque sessão, tenant e ACL do recurso dentro da implementação do tool.
- Ponha portão em efeitos colaterais de saída. Enviar, transferir, apagar, deploy em produção: motor de política ou confirmação humana.
- Rebaixe texto externo. Resultados de tools, páginas e e-mail não são system. Quando precisar, um Subagent só leitura devolve um resumo ao pai.
- Audite arguments. Registre nome do tool, parâmetros validados, quem autorizou, se foi negado.
Prompts ainda ajudam: diga que dados não são instruções, liste ações proibidas. São camada de apoio. Dez frases a mais de prompt não substituem um Schema ou um ACL que faltam.
Revisar contratos com ferramentas JSON locais
Antes de publicar, olhe três arquivos juntos: os parameters do tool / o inputSchema de MCP, um objeto de arguments do «caminho feliz», e uma amostra de propósito feia (chaves a mais, tipos errados, strings enormes). Sem segredos reais, sem dados reais de usuário.
- Validador JSON — a amostra é JSON legal, e satisfaz o seu Schema?
- JSON Diff — o que o modelo acrescentou além do objeto mínimo?
- Visualizador de árvore — o aninhamento está mais fundo do que deveria; um campo string esconde outra estrutura?
Nada sai do navegador. Serve para revisar um contrato prestes a ir para produção, e para comparar arguments de uma chamada que falhou. Estabilize nomes de campo e required, depois ligue o Host ou o MCP Server.
FAQ
JSON Schema consegue impedir Prompt Injection?
Não sozinho. Schema limita a forma e o intervalo dos arguments de um tool, e reduz a chance de uma string arbitrária virar uma ação arbitrária. Injeção indireta acontece quando o texto entra no contexto — você ainda precisa de procedência, rebaixamento e portões de alto impacto.
O modelo já usa Structured Output / modo strict. O Host ainda precisa validar?
Sim. As restrições do fornecedor valem na geração, e cada um suporta um subconjunto diferente de Schema. Decisões de segurança precisam acontecer antes da execução, com o seu validador e a sua autorização.
Qual o problema de passar o JSON do usuário direto para um tool?
Você pula o contrato. Campos a mais, confusão de tipos e texto aninhado chegam iguais em código com efeito colateral. Valide, depois projete; passe só campos da allowlist.
Dá para usar a saída de um MCP Server como system prompt?
Não. Descrições de tools/list, texto de Resources e resultados de tools/call são dados não confiáveis. Valide e reentre no diálogo com privilégio menor.
Qual é a linha de base mínima viável de segurança de um Agent?
JSON Schema estrito, revalidação no Host, allowlist de tools, autorização por usuário e portões nos efeitos colaterais de saída. Sem essas cinco, não ligue o Agent a dados de produção.
Como eu checo localmente se o JSON de um tool é perigoso?
Coloque Schema e amostras de arguments na caixa de ferramentas JSON para validação e Diff. Confirme required, additionalProperties e limites de comprimento/enum antes de ligar o Host ou o MCP Server.
Resumo
A superfície de ataque do Agent fica entre dados estruturados e a execução de tools: JSON malicioso fura o contrato, Prompt Injection desvia a intenção, Tool Calling vira intenção em efeito colateral, e um Schema frouxo abre a porta para os três. A pilha padrão de 2026 (Tools, MCP, Skills, Subagents) deixa Agents mais úteis — e faz «enganar uma invocação» valer mais do que «enganar uma resposta».
Defenda de dentro para fora: faça do JSON Schema um contrato duro; o Host valida, projeta e autoriza; mantenha tools pequenos; não trate texto externo como instrução. Prompts ajudam; não são a fronteira. Valide amostras localmente antes de publicar — modelos podem mudar; nomes de campo, required e «quem pode executar» não deveriam.