Início › Blog › Isolamento multi-cliente
Isolamento multi-cliente: como o IDOR vaza dados entre clientes do seu SaaS
12 de outubro de 2026 · ~9 min de leitura
Quase todo SaaS é multi-tenant: todos os seus clientes vivem no mesmo banco, separados por um tenant_id (ou user_id, org_id). O isolamento entre eles depende de uma verificação em cada acesso. Esquecer uma única é a falha nº 1 do OWASP API Top 10 — o IDOR / BOLA — e é a que mais transforma um SaaS novo em notícia ruim. Este é o guia da falha: por que acontece, os 7 padrões e onde a checagem tem que ficar.
IDOR / BOLA em uma frase
IDOR (Insecure Direct Object Reference) e BOLA (Broken Object Level Authorization) são o mesmo problema: a app usa um identificador vindo do cliente para buscar um objeto, mas não confirma que aquele objeto pertence a quem pediu. Autenticação (quem é você) está certa; autorização no nível do objeto (você pode ver este?) está faltando.
1. Autorizar por "logado", não por "é dono"
O padrão clássico. Passou pela autenticação, então lê qualquer objeto:
app.get('/invoices/:id', auth, async (req, res) => {
const inv = await db.invoices.findById(req.params.id);
res.json(inv); // ❌ nunca checa inv.tenantId
});
// ✅ a busca já é limitada ao tenant da sessão
const inv = await db.invoices.findOne({
id: req.params.id, tenantId: req.session.tenantId
});
if (!inv) return res.sendStatus(404);
2. Confiar no tenant_id que o cliente manda
Se o tenant vem de um parâmetro, header ou corpo da requisição, o cliente simplesmente escolhe outro e atravessa a fronteira. O tenant tem que vir da sessão/token do servidor, nunca da requisição.
// ❌ cliente escolhe o tenant const tid = req.query.tenantId || req.body.tenantId; // ✅ tenant vem do token verificado const tid = req.session.tenantId;
3. Achar que ID aleatório (UUID) é segurança
Trocar IDs sequenciais por UUID dificulta adivinhar, mas não é autorização. IDs vazam em URLs, logs, e-mails, referrers e respostas de API — o atacante raramente precisa adivinhar. UUID é defesa em profundidade; a checagem de dono continua obrigatória.
4. Esquecer o escopo nas listagens
Pior que vazar um objeto é vazar a coleção. Uma rota de listagem sem o filtro de tenant devolve a base toda de uma vez.
// ❌ lista tudo de todos const rows = await db.orders.find({}); // ✅ escopo obrigatório const rows = await db.orders.find({ tenantId: req.session.tenantId });
5. Recurso aninhado sem re-checar a cadeia
Em /projects/:pid/tasks/:tid é comum checar o projeto e confiar na tarefa — aí alguém passa um tid de outro projeto/tenant. Valide a cadeia inteira: a tarefa pertence ao projeto, e o projeto pertence ao tenant.
const task = await db.tasks.findOne({
id: tid, projectId: pid
});
if (!task || project.tenantId !== req.session.tenantId) return res.sendStatus(404);
6. Cache, storage e arquivos sem tenant na chave
O isolamento não é só do banco. Uma chave de cache user:settings sem o tenant serve os dados do cliente errado; um caminho de arquivo /uploads/fatura.pdf sem o tenant deixa um baixar o do outro. Coloque o tenant na chave de tudo que é compartilhado.
cache.get('settings') // ❌ compartilhado entre tenants cache.get(`t:${tid}:settings`) // ✅ isolado por tenant
7. Jobs, exportações e webhooks sem escopo
Relatórios agendados, exportações de CSV, reprocessamentos e handlers de webhook rodam fora do contexto da requisição — e é fácil esquecerem o tenant, misturando dados de clientes num mesmo arquivo ou e-mail. Todo processo em segundo plano tem que carregar e respeitar o tenant explicitamente.
Onde a checagem tem que ficar
Defesa em profundidade: aplique o isolamento na camada de dados — RLS do Supabase ou regras do Firebase, que barram mesmo se o código esquecer — e na camada de aplicação (o escopo por tenant em toda query). Uma não substitui a outra: a RLS é a rede de segurança; a checagem na app é a porta.
403/404 é um vazamento. Vale automatizar isso num teste de integração que roda no CI. Por que ferramentas de padrão não pegam isso: veja SAST e o que o Semgrep deixa passar.
Tem certeza de que um cliente não vê o outro?
O SAAS SHIELD revisa seu repositório procurando exatamente isso — autorização por objeto, escopo de tenant e RLS/regras — e devolve cada falha com local, evidência e correção.
Solicitar auditoria →