08 de out. de 2025·8 min de leitura

Design de busca global consciente de permissões sem vazamentos de dados

Aprenda a projetar uma busca global consciente de permissões com indexação rápida e verificações de acesso por registro rigorosas, para que os usuários obtenham resultados rápidos sem vazamentos.

Design de busca global consciente de permissões sem vazamentos de dados

Por que a busca global pode vazar dados

Busca global normalmente significa uma única caixa que varre o app inteiro: clientes, tickets, faturas, documentos, usuários e o que mais for usado. Ela costuma alimentar autocomplete e uma página de resultados rápida para que os usuários saltem direto para um registro.

O vazamento ocorre quando a busca retorna algo que o usuário não deveria saber que existe. Mesmo que ele não consiga abrir o registro, uma única linha como um título, o nome de uma pessoa, uma tag ou um trecho destacado pode revelar informação sensível.

A busca parece "apenas leitura", então equipes subestimam o risco. Mas ela pode divulgar dados por meio de títulos e pré-visualizações, sugestões do autocomplete, totais de resultados, facetas como "Clientes (5)", e até diferenças de tempo (rápida para alguns termos, mais lenta para outros).

Isso geralmente aparece depois, não no dia um. No começo, equipes lançam a busca quando só existe um papel, ou quando todo mundo vê tudo no banco de testes. À medida que o produto cresce, surgem papéis (suporte vs vendas, gerentes vs agentes) e recursos como caixas compartilhadas, notas privadas, clientes restritos e "somente minhas contas". Se a busca ainda depender das suposições antigas, ela começa a retornar pistas entre equipes ou entre clientes.

Um modo comum de falha é indexar "tudo" para velocidade e depois tentar filtrar resultados na aplicação após a consulta. Isso é tarde demais. O mecanismo de busca já decidiu o que bateu, e pode expor registros restritos por meio de sugestões, contagens ou campos parciais.

Imagine um agente de suporte que só deve ver tickets dos clientes atribuídos a ele. Ele digita "Acme" e o autocomplete mostra "Acme - Escalada legal" ou "Acme notificação de vazamento". Mesmo que ao clicar apareça "acesso negado", o título sozinho já é um vazamento.

O objetivo da busca global consciente de permissões é simples de dizer e difícil de implementar: retornar resultados rápidos e relevantes enquanto aplica as mesmas regras de acesso usadas ao abrir cada registro. Cada consulta deve se comportar como se o usuário só pudesse ver a sua fatia de dados, e a interface deve evitar vazar pistas extras (como contagens) fora dessa fatia.

O que você está indexando e o que precisa proteger

Busca global parece simples porque usuários digitam palavras e esperam respostas. Por baixo, você está criando uma nova superfície de exposição de dados. Antes de escolher um índice ou um recurso de banco, esclareça duas coisas: quais objetos você está buscando (entidades) e quais partes dessas entidades são sensíveis.

Uma entidade é qualquer registro que alguém possa querer encontrar rapidamente. Na maioria dos apps de negócio, isso inclui clientes, tickets de suporte, faturas, pedidos e arquivos (ou metadados de arquivos). Pode incluir também registros de pessoas (usuários, agentes), notas internas e objetos de sistema como integrações ou chaves de API. Se tem um nome, um ID ou um status que alguém possa digitar, tende a acabar na busca global.

Regras por registro vs regras por tabela

Regras por tabela são bruscas: ou você acessa toda a tabela, ou não. Exemplo: somente Finanças pode abrir a página de Faturas. Isso é fácil de raciocinar, mas falha quando pessoas diferentes devem ver linhas diferentes da mesma tabela.

Regras por registro decidem visibilidade linha a linha. Exemplo: um agente de suporte vê tickets atribuídos à sua equipe, enquanto um gerente vê todos os tickets de sua região. Outra regra comum é propriedade por tenant: em um app multitenant, o usuário só vê registros onde customer_id = their_customer_id.

São essas regras por registro que costumam vazar na busca. Se seu índice retorna um hit antes de você checar acesso à linha, você já revelou que algo existe.

O que "permitido ver" significa na prática

"Permitido" raramente é um simples sim/não. Normalmente combina propriedade (criado por mim, atribuído a mim), associação (minha equipe, meu departamento, meu papel), escopo (minha região, unidade de negócio, projeto), estado do registro (publicado, não arquivado) e casos especiais (clientes VIP, retenções legais, tags restritas).

Escreva essas regras em linguagem simples primeiro. Depois você vai transformar isso em um modelo de dados e checagens server-side.

Decida o que é seguro mostrar em uma pré-visualização de resultado

Resultados de busca frequentemente incluem um trecho de pré-visualização, e trechos podem vazar dados mesmo se o usuário não puder abrir o registro.

Um padrão seguro é mostrar apenas campos mínimos e não sensíveis até que o acesso seja confirmado: um nome de exibição ou título (às vezes mascarado), um identificador curto (como número do pedido), um status de alto nível (Aberto, Pago, Enviado), uma data (criado ou atualizado) e um rótulo genérico de entidade (Ticket, Fatura).

Exemplo concreto: se alguém busca "Acme merger" e existe um ticket restrito, retornar "Ticket: Acme merger rascunho - Legal" já é um vazamento. Um resultado mais seguro é "Ticket: Restrito" sem trecho, ou nenhum resultado, dependendo da sua política.

Definir isso bem desde o início facilita decisões posteriores: o que você indexa, como filtrar e o que está disposto a revelar.

Requisitos básicos para uma busca segura e rápida

Pessoas usam busca global quando estão com pressa. Se levar mais de um segundo, elas param de confiar e voltam ao filtro manual. Mas velocidade é só metade do trabalho. Uma busca rápida que vaza até um título de registro, nome de cliente ou assunto de ticket é pior do que não ter busca nenhuma.

A regra central é inegociável: aplique permissões no momento da consulta, não apenas na UI. Esconder uma linha depois de buscá-la já é tarde demais, porque o sistema já tocou dados que não deveria ter retornado.

O mesmo vale para tudo ao redor da busca, não só a lista final de resultados. Sugestões, top hits, contagens e até comportamento de "nenhum resultado" podem vazar informação. Autocomplete que mostra "Acme Renovação Contrato" para alguém que não pode abrir isso é um vazamento. Uma faceta que diz "12 faturas correspondentes" é um vazamento se o usuário só pode ver 3. Até o tempo de resposta pode vazar se matches restritos deixam a consulta mais lenta.

Uma busca global segura precisa de quatro coisas:

  • Correção: cada item retornado é permitido para este usuário, neste tenant, agora.
  • Velocidade: resultados, sugestões e contagens permanecem consistentemente rápidos, mesmo em larga escala.
  • Consistência: quando o acesso muda (atualização de papel, ticket reatribuído), o comportamento da busca muda de forma rápida e previsível.
  • Auditabilidade: você pode explicar por que um item foi retornado e registrar atividade de busca para investigações.

Mude a mentalidade: trate a busca como outra API de dados, não apenas como recurso da UI. Isso significa que as mesmas regras de acesso aplicadas nas páginas de listagem devem também valer na construção do índice, na execução de consultas e em todos os endpoints relacionados (autocomplete, buscas recentes, queries populares).

Três padrões de design comuns (e quando usá-los)

Uma caixa de busca é fácil de construir. Uma busca global consciente de permissões é mais difícil porque o índice quer retornar resultados instantaneamente, enquanto sua aplicação nunca deve revelar registros que o usuário não pode acessar, nem indiretamente.

Abaixo estão três padrões que equipes usam com frequência. A escolha certa depende de quão complexas são suas regras de acesso e quanto risco você pode tolerar.

Abordagem A: indexar apenas campos "seguros" e depois buscar com verificação de permissões. Você armazena um documento mínimo no índice de busca, como um ID mais um rótulo não sensível que é seguro mostrar a qualquer pessoa que alcance a UI de busca. Quando o usuário clica em um resultado, seu app carrega o registro completo do banco primário e aplica as regras reais de permissão lá.

Isso reduz o risco de vazamento, mas pode deixar a busca "magra" porque os usuários têm pouco contexto nos resultados. Também exige cuidado no texto da UI para que um rótulo "seguro" não exponha segredos acidentalmente.

Abordagem B: armazenar atributos de permissão no índice e filtrar lá. Você inclui campos como tenant_id, team_id, owner_id, flags de papel ou project_id em cada documento indexado. Cada consulta adiciona filtros que combinam com o escopo do usuário atual.

Isso fornece resultados ricos e rápidos e bom autocomplete, mas só funciona quando as regras de acesso podem ser expressas como filtros. Se permissões dependerem de lógica complexa (por exemplo, "atribuído OU de plantão esta semana OU parte de um incidente"), fica difícil manter isso correto.

Abordagem C: híbrido. Filtro grosso no índice, checagem final no banco. Você filtra no índice usando atributos estáveis e amplos (tenant, workspace, customer), então revalida permissões no conjunto pequeno de IDs candidatos no banco primário antes de devolver qualquer coisa.

Esse caminho é frequentemente o mais seguro para apps reais: o índice permanece rápido e o banco é a fonte da verdade.

Escolhendo um padrão

Escolha A quando quiser a configuração mais simples e puder conviver com snippets mínimos. Escolha B quando tiver escopos claros e estáticos (multitenant, acesso por equipe) e precisar de autocomplete muito rápido. Escolha C quando tiver muitos papéis, exceções ou regras por registro que mudam com frequência. Para dados de alto risco (RH, finanças, saúde), prefira C porque "quase certo" não é aceitável.

Passo a passo: projetando um índice que respeite regras de acesso

Execute um plano de testes focado em vazamentos
Suba um app de staging para testar vazamentos via contagens, facetas e estados vazios.
Testar

Comece escrevendo suas regras de acesso como você explicaria a um novo colega. Evite "admin vê tudo" a menos que seja realmente verdade. Explique os motivos: "Agentes de suporte veem tickets do tenant. Líderes de time também veem tickets da sua unidade. Somente o dono do ticket e o agente atribuído veem notas privadas." Se você não consegue dizer por que alguém pode ver um registro, terá dificuldade para codificar isso com segurança.

Em seguida, escolha um identificador estável e defina um documento de busca mínimo. O índice não deve ser uma cópia completa da linha do banco. Mantenha apenas o que é necessário para encontrar e exibir na lista de resultados, como título, status e talvez um trecho curto não sensível. Coloque campos sensíveis atrás de uma segunda busca que também cheque permissões.

Depois decida quais sinais de permissão você pode filtrar rapidamente. São atributos que limitam acesso e podem ser armazenados em cada documento indexado, como tenant_id, org_unit_id e um pequeno número de flags de escopo. O objetivo é que cada consulta aplique filtros antes de retornar resultados, inclusive no autocomplete.

Um fluxo prático:

  1. Defina as regras de visibilidade para cada entidade (tickets, clientes, faturas) em linguagem simples.
  2. Crie um esquema de documento de busca com record_id mais apenas campos seguros e pesquisáveis.
  3. Adicione campos filtráveis de permissão (tenant_id, org_unit_id, visibility_level) a cada documento.
  4. Trate exceções com grants explícitos: guarde uma allowlist (IDs de usuário) ou IDs de grupo para itens compartilhados.

Itens compartilhados e exceções são onde designs quebram. Se um ticket pode ser compartilhado entre equipes, não "apenas adicione um booleano." Use grants explícitos que possam ser checados por filtros. Se a allowlist for grande, prefira grants baseados em grupos em vez de usuários individuais.

Manter o índice sincronizado sem surpresas

Construa busca global segura
Construa uma API de busca consciente de permissões com verificações server-side que você controla.
Começar a construir

Uma experiência de busca segura depende de uma coisa chata feita bem: o índice deve refletir a realidade. Se um registro é criado, alterado, deletado ou suas permissões mudam, os resultados de busca devem acompanhar de forma rápida e previsível.

Acompanhe criação, atualização e exclusão

Trate indexação como parte do ciclo de vida dos dados. Um modelo mental útil é: toda vez que a fonte da verdade muda, você emite um evento e o indexador reage.

Abordagens comuns incluem triggers no banco, eventos da aplicação ou uma fila de jobs. O que importa é não perder eventos. Se sua aplicação consegue salvar o registro mas falha ao indexá-lo, você terá comportamento confuso como "sei que existe mas a busca não encontra."

Mudanças de permissão são mudanças no índice

Muitos vazamentos ocorrem quando o conteúdo atualiza corretamente, mas os metadados de acesso não. Mudanças de permissão vêm de atualizações de papel, movimentações de equipe, transferências de propriedade, reassociação de cliente ou um ticket sendo mesclado em outro caso.

Faça mudanças de permissão serem eventos de primeira classe. Se sua busca consciente de permissões depende de filtros por tenant ou equipe, garanta que documentos indexados incluam os campos necessários para aplicar isso (tenant_id, team_id, owner_id, allowed_role_ids). Quando esses campos mudam, reindexe.

A parte complicada é o raio de alcance. Uma mudança de papel pode afetar milhares de registros. Planeje um caminho de reindexação em lote que tenha progresso, retries e uma forma de pausar.

Planeje para consistência eventual

Mesmo com bons eventos, haverá uma janela onde a busca fica atrasada. Decida o que os usuários devem ver nos primeiros segundos após uma mudança.

Duas regras ajudam:

  • Seja consistente sobre atrasos. Se a indexação normalmente termina em 2–5 segundos, defina essa expectativa quando fizer sentido.
  • Prefira ausência a vazamento. É mais seguro que um registro recém-concedido apareça um pouco tarde do que um registro recém-revogado continue aparecendo.

Adicione um fallback seguro quando o índice estiver desatualizado

Busca serve para descoberta, mas ver detalhes é onde vazamentos fazem mal. Faça uma segunda checagem de permissões ao ler antes de mostrar campos sensíveis. Se um resultado escapou porque o índice estava desatualizado, a página de detalhes ainda deve bloquear o acesso.

Um padrão bom é: mostrar snippets mínimos na busca e re-checar permissões quando o usuário abre o registro (ou expande uma pré-visualização). Se a checagem falhar, mostre uma mensagem clara e remova o item do conjunto visível na próxima atualização.

Erros comuns que causam vazamentos de dados

A busca pode vazar dados mesmo quando sua página de "abrir registro" está bloqueada. Um usuário pode nunca clicar em um resultado e ainda assim aprender nomes, IDs de cliente ou o tamanho de um projeto oculto. Busca global consciente de permissões tem que proteger não só documentos, mas também pistas sobre documentos.

Autocomplete é uma fonte frequente de vazamentos. Sugestões costumam ser alimentadas por um lookup prefix rápido que pula checagens completas de permissão. A UI parece inofensiva, mas uma única letra digitada pode revelar um nome de cliente ou email de um funcionário. Autocomplete deve rodar com o mesmo filtro de acesso da busca completa, ou ser construído a partir de um conjunto pré-filtrado de sugestões (por exemplo, por tenant e por papel).

Contagens de facetas e banners "Sobre 1.243 resultados" são outro vazamento silencioso. Contagens confirmam que algo existe mesmo se você ocultar os registros. Se você não consegue calcular contagens sob as mesmas regras de acesso, mostre menos detalhes ou omita contagens.

Caching é outro culpado comum. Caches compartilhados entre usuários, papéis ou tenants podem criar "fantasmas de resultado", onde um usuário vê resultados gerados para outro. Isso pode acontecer com caches de borda, caches no nível da aplicação e caches em memória dentro de um serviço de busca.

Armadilhas de vazamento para checar cedo:

  • Autocomplete e buscas recentes são filtradas pelas mesmas regras que a busca completa.
  • Contagens de facetas e totais são computadas após permissões.
  • Chaves de cache incluem tenant ID e uma assinatura de permissão (papel, equipe, user ID).
  • Logs e analytics não armazenam queries brutas ou trechos de resultados para dados restritos.

Finalmente, cuidado com filtros amplos demais. "Filtrar só por tenant" é o erro clássico em multitenant, mas também acontece dentro de um mesmo tenant: filtrar por "departamento" quando o acesso é por registro. Exemplo: um agente busca "reembolso" e obtém resultados de todos os clientes no tenant, incluindo contas VIP que deveriam ser visíveis apenas a uma equipe menor. A solução é simples em princípio: aplique regras em nível de linha em todo caminho de consulta (busca, autocomplete, facetas, exportações), não apenas na visualização do registro.

Detalhes de privacidade e segurança que as pessoas esquecem

Faça da busca uma API real
Crie um backend que aplique as mesmas regras de acesso para busca e visualização de registros.
Gerar backend

Muitos designs se concentram em "quem pode ver o quê", mas vazamentos também ocorrem pelas bordas: estados vazios, tempo de resposta e pequenas pistas na UI. Busca consciente de permissões tem que ser segura mesmo quando não retorna nada.

Um vazamento fácil é confirmação por ausência. Se um usuário não autorizado pesquisa um nome de cliente, ID de ticket ou email específico e recebe uma mensagem especial como "Sem acesso" ou "Você não tem permissão", você confirmou que o registro existe. Trate "nenhum resultado" como o resultado padrão tanto para "não existe" quanto para "existe mas não está permitido". Mantenha tempo de resposta e mensagens consistentes para que ninguém possa adivinhar pelo desempenho.

Matches parciais sensíveis

Autocomplete e busca enquanto digita são onde a privacidade escorrega. Matches parciais em emails, telefones e documentos de identificação podem expor mais do que o pretendido. Decida upfront como esses campos se comportam.

Um conjunto prático de regras:

  • Exija correspondência exata para campos de alto risco (email, telefone, IDs).
  • Evite mostrar trechos destacados que revelem texto oculto.
  • Considere desativar autocomplete para campos sensíveis totalmente.

Se mostrar até um caractere ajuda alguém a adivinhar dados, trate isso como sensível.

Controles contra abuso que não criem novos riscos

Endpoints de busca são ideais para ataques de enumeração: tentar várias queries para mapear o que existe. Adicione rate limits e detecção de anomalias, mas seja cuidadoso com o que você armazena. Logs que incluem queries brutas podem virar uma segunda fonte de vazamento.

Mantenha simples: rate limit por usuário, por IP e por tenant; registre contagens, tempos e padrões grosseiros (não o texto bruto da query); alerte sobre repetições de "quase acertos" (como IDs sequenciais); e bloqueie ou peça verificação após falhas repetidas.

Faça seus erros monótonos. Use a mesma mensagem e estado vazio para "nenhum resultado", "não permitido" e "filtros inválidos". Quanto menos sua UI disser, menos ela pode revelar acidentalmente.

Exemplo: time de suporte buscando tickets entre clientes

Controle snippets e pré-visualizações
Use lógica de negócio visual para re-verificar acesso antes de mostrar pré-visualizações sensíveis.
Construir fluxo

Uma agente de suporte, Maya, trabalha em uma equipe que atende três contas de cliente. Ela tem uma caixa de busca no cabeçalho do app. O produto tem um índice global sobre tickets, contatos e empresas, mas cada resultado deve obedecer regras de acesso.

Maya digita "Alic" porque um chamador disse que o nome é Alice. O autocomplete mostra algumas sugestões. Ela clica "Alice Nguyen - Ticket: Reset de senha." Antes de abrir qualquer coisa, o app re-checa o acesso para aquele registro. Se o ticket ainda estiver atribuído à equipe dela e seu papel permitir, ela chega ao ticket.

O que Maya vê em cada passo:

  • Caixa de busca: sugestões aparecem rápido, mas só para registros que ela pode acessar agora.
  • Lista de resultados: assunto do ticket, nome do cliente, última atualização. Sem placeholders "você não tem acesso".
  • Detalhes do ticket: visão completa carrega apenas após uma checagem server-side de permissões. Se o acesso mudou, o app mostra "Ticket não encontrado" (não "proibido").

Compare com Leo, um novo agente em treinamento. Seu papel só vê tickets marcados "Público ao Suporte" e apenas para um cliente. Leo digita a mesma query, "Alic." Ele vê menos sugestões, e nenhuma das ausentes é sugerida. Não há contagem "5 resultados" que revelaria que há outras correspondências. A UI simplesmente mostra o que ele pode abrir.

Mais tarde, um gerente reatribui "Alice Nguyen - Reset de senha" da equipe da Maya para uma equipe de escalonamento especializada. Em uma janela curta (geralmente segundos a alguns minutos, dependendo da sua abordagem de sincronização), a busca da Maya para de retornar aquele ticket. Se ela tinha a página de detalhes aberta e atualizar, o app re-checa permissões e o ticket desaparece.

Esse é o comportamento desejado: digitação rápida e resultados rápidos, sem cheiro de dados vazando por contagens, trechos ou entradas desatualizadas no índice.

Checklist e próximos passos para implementar com segurança

Busca global consciente de permissões só está "pronta" quando as bordas chatas estão seguras. Muitos vazamentos acontecem em lugares que parecem inofensivos: autocomplete, contagens de resultados e exportações.

Verificações rápidas de segurança

Antes de lançar, percorra essas checagens com dados reais, não amostras:

  • Autocomplete: nunca sugira um título, nome ou ID que o usuário não possa abrir.
  • Contagens e facetas: se mostrar totais ou contagens agrupadas, calcule-as após permissões (ou omita contagens).
  • Exportações e ações em massa: exportar a "busca atual" deve checar acesso por linha na hora da exportação.
  • Ordenação e destaque: não ordene nem destaque usando campos que o usuário não tem permissão para ver.
  • "Não encontrado" vs "proibido": para entidades sensíveis, considere a mesma forma de resposta para que usuários não possam confirmar existência.

Um plano de testes que você pode rodar

Crie uma pequena matriz de papéis (papéis x entidades) e um conjunto de dados com casos propositalmente complicados: registros compartilhados, acesso recentemente revogado e homônimos entre tenants.

Teste em três etapas: (1) testes da matriz de papéis onde você verifica que registros negados nunca aparecem em resultados, sugestões, contagens ou exportações; (2) testes "tentar quebrar" onde você cola IDs, pesquisa por email ou telefone e tenta matches parciais que devem retornar nada; (3) testes de tempo e cache onde você muda permissões e confirma que resultados atualizam rapidamente sem sugestões desatualizadas.

Operacionalmente, planeje para o dia em que resultados de busca "pareçam errados." Registre o contexto da query (usuário, papel, tenant) e os filtros de permissão aplicados, mas evite armazenar strings de consulta sensíveis ou trechos. Para depuração segura, construa uma ferramenta admin-only que explique por que um registro bateu e por que foi permitido.

Se você está construindo sobre AppMaster (appmaster.io), uma abordagem prática é manter a busca como flow server-side: modele entidades e relações no Data Designer, aplique regras de acesso em Business Processes e reutilize essa mesma checagem de permissões para autocomplete, lista de resultados e exportações, assim há apenas um lugar para acertar.

Fácil de começar
Criar algo espantoso

Experimente o AppMaster com plano gratuito.
Quando estiver pronto, você poderá escolher a assinatura adequada.

Comece
Design de busca global consciente de permissões sem vazamentos de dados | AppMaster