Em resumo: A entrada da sua API é uma superfície de ataque. Teste-a como tal: envie campos superdimensionados, tipos incorretos, corpos malformados e strings de injeção. Verifique se o endpoint responde com
4xx, nunca com5xx. Use validação de esquema comadditionalProperties: false, enums e limites de tamanho. Execute esses testes no CI a cada mudança. Isso é ainda mais importante com agentes de IA, que podem gerar e encaminhar payloads na velocidade da máquina.
A maioria das suítes prova apenas que a API funciona quando o cliente se comporta bem: envia um corpo válido, recebe 200 e passa no teste. Isso não mostra o que acontece com entrada hostil. Considere como não confiável qualquer dado que seu endpoint não criou: corpos de requisição, query strings, cabeçalhos, uploads, webhooks e JSON gerado por agentes de IA.
Em julho de 2026, o Hugging Face descreveu um incidente cujo vetor de entrada eram dados, não credenciais roubadas. Abordamos as lições dessa violação separadamente; aqui, o foco é implementar testes que simulem entradas hostis e executá-los automaticamente. Use o Top 10 de Segurança de API OWASP como referência. O Apidog pode ajudar a definir contratos e executar cenários, mas os exemplos se aplicam a qualquer stack.
A entrada é uma superfície de ataque
Validação não é apenas uma melhoria de UX. Cada campo aceito por uma API é uma suposição que o cliente pode quebrar.
Exemplos comuns:
- Um
limitesperado como inteiro pequeno chega como999999999. - Um
filenameesperado como nome simples chega como../../etc/passwd. - Um objeto
configesperado como configuração chega com instruções ou campos inesperados.
Trate testes de segurança como testes negativos direcionados. Para cada campo, pergunte:
Qual é a pior entrada que ainda cabe neste formato?
Esse hábito cobre boa parte das práticas de segurança de API.
Como “carregar dados” pode virar “executar código”
No incidente do Hugging Face, conjuntos de dados maliciosos acionaram um carregador com execução remota de código, e uma injeção de template estava presente em uma configuração de dataset. Veja o relatório de incidente de segurança.
O padrão é importante:
- O endpoint aceita algo descrito como dado.
- O carregamento passa por um caminho que interpreta esse dado.
- O dado controlado pelo atacante vira instrução executável.
Esse risco existe em endpoints que aceitam:
- nomes de loaders;
- formatos de importação;
- templates;
- objetos serializados;
- blocos de configuração;
- caminhos de arquivo;
- opções que acabam em comandos de shell.
Se você nunca enviou uma configuração hostil para esses endpoints, ainda não verificou se ela permanece inerte.
Use validação de esquema como controle de segurança
Um esquema rigoroso na borda impede que dados inválidos cheguem à lógica de negócio. Com JSON Schema, o contrato deixa de ser apenas documentação e passa a ser um filtro ativo.
Exemplo de esquema para configuração de dataset:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": ["loader", "name"],
"properties": {
"loader": {
"enum": ["csv", "json", "parquet"]
},
"name": {
"type": "string",
"maxLength": 128,
"pattern": "^[\\w .-]+$"
},
"rows": {
"type": "integer",
"minimum": 0,
"maximum": 1000000
}
}
}
Esse contrato aplica quatro controles distintos:
| Controle | O que bloqueia |
|---|---|
additionalProperties: false |
Campos inesperados, como um template inserido no payload |
enum em loader
|
Valores não permitidos, como pickle://
|
maxLength |
Strings enormes que podem consumir memória |
pattern |
Caracteres e formatos não suportados, como {{ ou '; DROP TABLE
|
A validação de esquema não elimina toda vulnerabilidade. Um valor pode ser válido no esquema e ainda ser perigoso em uma consulta SQL ou template. Mas ela bloqueia uma classe crítica de falhas: “não verificamos o que este endpoint aceita”.
Escreva testes negativos que comprovem a recusa
Um teste de caminho feliz prova que uma entrada válida gera uma saída válida.
Um teste negativo prova que uma entrada inválida recebe uma recusa controlada.
Para cada campo, crie casos que cubram:
- tipo incorreto;
- campo obrigatório ausente;
- campo proibido presente;
- valor fora do intervalo;
- string longa demais;
- JSON malformado;
- strings de injeção compatíveis com o contexto.
Em cada resposta, valide pelo menos:
- O status é um
4xx, geralmente400,413ou422. - O status não é
5xx. - Quando aplicável, nenhum efeito colateral ocorreu.
Evite acoplar o teste ao texto exato da mensagem de erro. Prefira status code, estrutura de resposta e ausência de efeitos no banco, fila ou armazenamento.
A lista de verificação de testes de segurança de API pode servir como ponto de partida para essa matriz de casos.
Adicione testes para classes de injeção
Você não precisa testar todas as variações possíveis. Comece com uma sonda fixa por classe de injeção. Isso torna regressões visíveis no CI.
Injeção SQL
Envie uma string como:
1); DROP TABLE datasets;--
Use-a em campos que possam chegar a consultas, filtros ou ordenação.
Resultado esperado:
-
400ou uma resposta vazia, dependendo do contrato; - nenhum erro interno de banco;
- nunca um
500.
Mantenha consultas parametrizadas mesmo quando houver validação de esquema.
Injeção de template
Envie payloads como:
{{ 7*7 }}
{{ config.__class__ }}
Teste campos de nome, rótulo, descrição e configuração.
Se a resposta contiver 49, sua entrada foi avaliada por um mecanismo de template. Isso deve ser tratado como uma falha crítica.
Desserialização insegura e loaders remotos
Teste valores inesperados em campos de formato ou loader:
{
"loader": "pickle://s3/models/payload.pkl"
}
A API deve usar uma allowlist explícita. Não tente “adivinhar” formatos ou carregar handlers dinamicamente com base no valor enviado pelo cliente.
Injeção de comando
Teste campos que possam virar argumento de shell, caminho ou opção de ferramenta:
; id
$(id)
Campos de filename, conversão, exportação e importação merecem atenção especial.
Um 200 que retorna saída de comando não é apenas um bug: é uma descoberta crítica.
Para ampliar a cobertura depois, use ferramentas de detecção automatizada de vulnerabilidades de API. Ainda assim, os casos manuais são a forma mais rápida de capturar falhas óbvias.
Teste payloads superdimensionados e malformados
Nem toda entrada hostil é uma string de injeção. Muitas falhas aparecem antes da validação da aplicação, durante parsing ou alocação de memória.
Teste pelo menos:
- um campo com 5 MB de caracteres;
- um array JSON com milhões de itens;
- JSON truncado;
- JSON com vírgula final;
- JSON profundamente aninhado;
- corpos incompatíveis com o
Content-Type.
Exemplos de comportamento esperado:
| Entrada | Resposta esperada |
|---|---|
| Corpo acima do limite | 413 Payload Too Large |
| JSON inválido | 400 Bad Request |
| Tipo incompatível |
400 ou 415 Unsupported Media Type
|
| JSON muito profundo | Rejeição rápida, sem travar workers |
Também teste confusão de tipo de conteúdo:
Content-Type: application/json
Com corpo XML:
<dataset><name>test</name></dataset>
E o inverso: XML declarado com JSON, ou JSON enviado como text/plain.
O servidor deve exigir coerência entre cabeçalho e corpo antes de analisar o payload.
Por que agentes de IA aumentam o risco
Agentes de IA não mudam a natureza do problema; eles mudam escala e velocidade.
Um agente pode:
- gerar valores de entrada que nenhum desenvolvedor antecipou;
- repetir chamadas automaticamente;
- encadear milhares de requisições em segundos;
- encaminhar dados recebidos de documentos, webhooks ou sistemas externos;
- transportar instruções ocultas através de limites de confiança.
Um documento ou dataset contaminado pode acabar virando uma chamada real à sua API. Esse é o padrão em que “carregar dados” se torna “executar código”.
Leia mais sobre esse tipo de fluxo em injeção de prompt para equipes de API.
A defesa continua sendo a mesma:
- valide na borda;
- restrinja o contrato;
- recuse entradas inesperadas;
- registre falhas;
- automatize os testes no CI.
Implemente uma suíte negativa com pytest
O exemplo abaixo envia configurações hostis para um endpoint de staging e verifica se a API recusa o payload de forma controlada.
import httpx
import pytest
BASE = "https://staging.internal/v1"
HOSTILE_CONFIGS = [
{"loader": "pickle://s3/models/payload.pkl", "format": "auto"},
{"loader": "csv", "name": "{{ 7*7 }}"},
{"loader": "csv", "name": "{{ config.__class__ }}"},
{"loader": "csv", "filter": "1); DROP TABLE datasets;--"},
{"loader": "csv", "name": "A" * 5_000_000},
]
@pytest.mark.parametrize("config", HOSTILE_CONFIGS)
def test_dataset_config_is_refused(config):
response = httpx.post(
f"{BASE}/datasets",
json={"config": config},
timeout=10,
)
assert response.status_code in (400, 413, 422), response.text
assert response.status_code < 500, (
"5xx indica que o payload alcançou lógica que deveria ser inacessível"
)
assert "49" not in response.text, (
"Template renderizado: possível injeção de template no servidor"
)
Ajuste os status aceitos ao contrato da sua API. Por exemplo, se o endpoint usa 415 para tipo de conteúdo inválido, inclua esse código nos testes específicos dessa condição.
Também vale incluir verificações de efeito colateral. Por exemplo, após enviar um payload inválido, confirme que nenhum dataset foi criado:
def test_invalid_config_creates_no_dataset():
payload = {
"config": {
"loader": "pickle://s3/models/payload.pkl"
}
}
response = httpx.post(f"{BASE}/datasets", json=payload, timeout=10)
assert response.status_code in (400, 422)
datasets = httpx.get(f"{BASE}/datasets", timeout=10).json()
assert all(item["loader"] != "pickle://s3/models/payload.pkl" for item in datasets)
Execute a suíte no CI
Faça esses testes rodarem em todo push e pull request. Um workflow mínimo de GitHub Actions:
name: api-abuse-tests
on: [push, pull_request]
jobs:
negative-input:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pytest tests/negative_input.py -q
Execute contra staging ou um ambiente isolado. Não rode payloads de estresse, injeção de comando ou requisições gigantes diretamente em produção.
No Apidog, você pode definir o endpoint a partir de um contrato OpenAPI e salvar cenários negativos ao lado dos cenários de caminho feliz:
- campo superdimensionado;
- tipo incorreto;
- propriedade adicional;
- loader fora da allowlist;
- payload malformado;
- strings de injeção.
Depois, execute os mesmos cenários no CI pela CLI do Apidog. Assim, uma alteração que enfraqueça a validação falha a build antes do deploy. Para começar, baixe o Apidog e adicione um cenário negativo a um endpoint existente.
O Apidog é uma ferramenta de design, teste, mock e documentação. Ele não substitui WAF, SIEM ou filtragem de tráfego em tempo real. O objetivo é deixar explícito o que o endpoint aceita e garantir, continuamente, que entradas fora desse contrato sejam recusadas.
Perguntas frequentes
Qual é a diferença entre teste negativo e fuzzing?
Teste negativo usa entradas ruins selecionadas intencionalmente, uma para cada falha relevante. Fuzzing envia grandes volumes de entradas aleatórias ou mutadas para descobrir casos não antecipados.
Comece com testes negativos: são rápidos, determinísticos e adequados para CI. Adicione fuzzing para ampliar a busca.
Esses testes devem rodar em produção?
Não. Execute-os em staging ou em um ambiente isolado.
Payloads superdimensionados, JSON profundamente aninhado e sondas de injeção podem estressar o sistema ou alterar dados se existir uma falha.
Um WAF não resolve esse problema?
Um WAF é uma camada útil de defesa em profundidade, mas não substitui a validação no aplicativo.
O WAF não conhece toda a sua lógica de negócio, regras podem ser contornadas e configurações podem falhar. O endpoint precisa recusar entradas inválidas por conta própria.
Quantos casos negativos são necessários por endpoint?
Tenha, no mínimo, um caso por campo para cada classe de falha aplicável:
- tipo incorreto;
- valor fora de intervalo;
- valor muito longo;
- campo obrigatório ausente;
- campo proibido presente;
- payload de injeção compatível com o contexto.
O objetivo é cobrir classes de risco, não maximizar a quantidade bruta de testes.
Validação de esquema impede injeção completamente?
Não. Ela reduz muito a superfície de ataque ao bloquear formatos inesperados, campos extras e tamanhos abusivos.
Ainda mantenha:
- consultas parametrizadas;
- desserialização segura;
- allowlists para loaders e formatos;
- codificação de saída;
- isolamento de execução;
- limites de tamanho e timeout.
O esquema é a primeira barreira: ele impede que entradas que sua API não suporta cheguem às camadas que precisam defendê-las.
Top comments (0)