30 de out. de 2025·8 min de leitura

Breadcrumbs seguros no Sentry: relatórios de erro sem vazamento de dados pessoais

Configure breadcrumbs seguros no Sentry com sanitizadores, rastreamento de versões e contexto rico, para que erros sejam acionáveis sem registrar segredos ou dados pessoais.

Breadcrumbs seguros no Sentry: relatórios de erro sem vazamento de dados pessoais

O que dá errado com relatórios de erro e privacidade

Relatórios de erro devem ajudar você a consertar bugs mais rápido. Mas frequentemente coletam mais do que você pretendia, especialmente depois que você adiciona breadcrumbs.

Breadcrumbs são pequenas anotações sobre o que aconteceu pouco antes de uma falha: qual tela abriu, qual botão foi clicado, qual chamada de API falhou. Eles são úteis porque transformam um erro vago em uma sequência que você pode reproduzir.

O problema é que muitos apps tratam breadcrumbs como logs normais. Dados privados entram silenciosamente. Um breadcrumb pode conter uma URL completa com parâmetros de query, dump de headers ou o payload de um formulário. Isso pode expor dados pessoais (emails, telefones, endereços) e segredos (chaves de API, cookies de sessão, tokens de reset). Uma vez que esses dados chegam a uma ferramenta de erro, podem ser armazenados, pesquisáveis e visíveis para toda a equipe.

Pontos de vazamento costumam parecer inofensivos na revisão de código:

  • URLs com identificadores ou tokens de reset na query string
  • Headers como Authorization, Cookie ou X-API-Key
  • Campos de formulários de cadastro e checkout
  • Variáveis GraphQL ou corpos JSON de requisição
  • Logs de debug que imprimem variáveis de ambiente ou config

Um modelo mental simples ajuda: colete comportamento, não identidade. Registre que “cadastro falhou após envio” e “POST /api/signup retornou 500”, não o email do usuário, o corpo completo da requisição ou qualquer token.

Isso piora em protótipos gerados por IA. É comum ver helpers que imprimem objetos inteiros “só para debugar”, incluindo segredos e registros de usuário. Corrigir o crash é só metade do trabalho. Prevenir um vazamento silencioso de dados é a outra metade.

O que capturar (e o que nunca capturar)

Bons relatórios de erro focam no menor conjunto de fatos que explica o que quebrou. Se você pode responder “qual ação aconteceu, onde falhou e qual versão a entregou?”, normalmente tem contexto suficiente para corrigir o bug sem coletar dados pessoais.

Ajuda separar:

  • Eventos de negócio: o que o usuário está tentando fazer (criar conta, enviar arquivo, finalizar compra)
  • Eventos técnicos: o que o app fez (mudança de rota, requisição API, validação falhou)

Para breadcrumbs PII-safe no Sentry, prefira eventos técnicos mais um rótulo de negócio grosseiro. Evite a história completa do usuário.

Uma política prática de captura:

  • Seguro capturar: nome da tela/rota, ID do botão (não o texto do botão), chaves de feature flag, caminho do endpoint da API (sem query string), método HTTP e código de status, tempo (ms), contador de retry, estado do app como offline/online.
  • Arriscado: URLs completas com query params, corpos de request/response, campos preenchidos pelo usuário, email/telefone/endereço, mensagens de erro brutas que podem ecoar o input.
  • Nunca deve sair do app: senhas, magic links, tokens de sessão, refresh tokens, chaves de API, dados de cartão de pagamento, documentos governamentais.

Um exemplo concreto para um cadastro que quebrou:

  • Breadcrumb seguro: “POST /api/signup -> 400, validation_failed, release 1.8.3”
  • Breadcrumb arriscado: “POST /api/signup body={email:..., password:...}”

O primeiro diz onde olhar. O segundo cria um vazamento de dados.

Uma classificação simples de dados para breadcrumbs e erros

Breadcrumbs PII-safe funcionam melhor quando a equipe compartilha uma regra: todo valor que você captura é ou seguro por padrão, seguro só após redacção, ou nunca permitido. Se alguém consegue nomear a categoria em segundos, você evita vazamentos do tipo “achamos que estava ok”.

Três categorias que a maioria das equipes consegue seguir

  • Nunca capturar: credenciais e segredos (senhas, tokens de sessão, chaves de API, headers de autenticação, chaves privadas) e dados altamente sensíveis (número completo de cartão, CVV, dados de saúde).
  • Capturar apenas em forma reduzida: dados pessoais que identificam alguém (email, telefone, IP, nome completo, endereço). Se realmente precisar, faça hash, trunque ou substitua por uma referência interna estável.
  • Seguro capturar: contexto técnico que ajuda no debug (feature flags, nome da rota, botão clicado, região do servidor, código de erro).

Exemplos que costumam funcionar:

  • Email: forma reduzida (ou não capturar)
  • user_id interno: normalmente ok se não for previsível
  • Endereço IP: frequentemente considerado dado pessoal
  • Token de sessão: nunca
  • Chave de API: nunca

Uma política de redacção que as pessoas realmente vão seguir

Mantenha curta e consistente entre logs, breadcrumbs e eventos de erro:

  • Liste os campos exatos que você permite (por exemplo: user_id, org_id, release, route, feature).
  • Liste os campos que você sempre redige (token, password, authorization, cookie, secret, key).
  • Indique onde esses campos aparecem (corpo da requisição, headers, query string, local storage, inputs da UI).

O maior modo de falha é “logar o objeto inteiro”. Se você encontrar esse padrão em um codebase herdado, trate como urgente: remova primeiro, depois acrescente guardrails para que não volte.

Passo a passo: configuração base do Sentry com padrões seguros

Escolha uma ferramenta de reporte de erros e use-a de forma consistente no app. Sentry é comum, mas a mesma abordagem vale para outras. O objetivo é simples: todo erro deve indicar claramente onde aconteceu e qual build o entregou.

Comece padronizando ambientes e usando-os em todo lugar: dev para trabalho local, staging para testes pré-release e prod para usuários reais. Faça do ambiente um valor de configuração, não algo que as pessoas digitem à mão.

Inicialize o SDK com padrões seguros e apenas as integrações que você precisa. Uma configuração mínima no navegador pode ser assim:

Sentry.init({
  dsn: "…",
  environment: process.env.APP_ENV, // dev | staging | prod
  release: process.env.APP_RELEASE,  // e.g., git sha or build id

  tracesSampleRate: 0.1,   // performance sampling (start low)
  sampleRate: 1.0,         // error events (usually keep at 100%)

  maxBreadcrumbs: 50,
  maxValueLength: 250,
});

Habilite breadcrumbs que ajudem a reproduzir o que aconteceu sem transformar seus logs em um despejo de dados. Fontes padrão úteis são mudanças de navegação, cliques do usuário, chamadas de API (método + rota, não URLs completas) e erros do console.

Controle o volume para não se afogar em ruído ou custos. Alguns limites evitam isso:

  • Limite breadcrumbs (por exemplo 50) para que uma página ruidosa não domine a timeline.
  • Trunque strings longas (por exemplo 250 chars) para reduzir vazamentos acidentais.
  • Amostre dados de performance (comece em 5–10%) até entender o que é necessário.
  • Adicione rate limiting básico no cliente para que um loop quebrado não envie milhares de eventos.

Sanitizadores e allowlists para manter PII fora

Sanitização funciona melhor quando você começa por uma allowlist. Em vez de tentar bloquear todo segredo possível, decida quais campos de breadcrumb e erro você aceita e descarte o resto. Essa é a forma mais segura de manter breadcrumbs úteis sem coletar dados privados.

Um padrão prático é manter:

  • Nome do evento e uma mensagem curta
  • Um código de erro estável (se houver)
  • Um template de rota (como /users/:id)
  • Tags não pessoais (release, environment, feature flag)

Considere todo o resto suspeito até provar que é útil.

Depois, adicione sanitizadores rígidos para armadilhas previsíveis:

  • Remova/chaveie keys que frequentemente carregam segredos: password, pass, token, access_token, refresh_token, authorization, cookie, session, api_key
  • Redija headers de requisição por padrão e allowliste só os que realmente precisa (geralmente nenhum)
  • Remova query strings e fragments (?… e #…) a menos que haja uma razão específica e revisada para mantê-los
  • Normalize identificadores de usuário: use um id interno ou um hash unidirecional, não email/telefone cru
  • Descarte corpos de requisição a menos que haja uma allowlist restrita para campos específicos

Exemplo: um bug no cadastro.

  • Breadcrumb que vaza: “POST /signup?email=[email protected]
  • Breadcrumb mais seguro: “POST /signup (validation_failed, field=email)”, mais um user id como u_18429 ou hash_9f2c…

Você ainda vê o que aconteceu, mas não está armazenando dados pessoais.

Rastreamento de release que torna erros acionáveis

Refactor away spaghetti logging
Refatoramos código bagunçado para que regras de logging seguro se mantenham enquanto o app cresce.

Se um relatório de erro não diz qual versão o entregou, fica difícil consertar rápido. O rastreamento de release liga cada evento e trilha de breadcrumbs a um build para que você possa responder rápido: isso apareceu depois do último deploy?

Anexe um identificador de release a cada evento. Muitas equipes fazem isso na inicialização do app para que a release esteja presente mesmo se o crash ocorrer na primeira tela.

Uma regra de nomeação de releases que se mantém simples

Escolha um formato de nome que combine com seu processo de build e deploy. Consistência importa mais que criatividade.

  • Use a mesma string de release no frontend e backend quando possível
  • Prefira um SHA do git ou número de build
  • Adicione uma tag de ambiente só se sua ferramenta não separar ambientes por conta própria
  • Nunca inclua emails de usuário, nomes de tenant ou URLs de requisição no nome da release

Com isso, breadcrumbs PII-safe ficam mais úteis: você pode comparar “mesmo fluxo, versão diferente” sem inspecionar dados pessoais.

Marque deploys para que picos coincidam com mudanças

Marcar deploys transforma gráficos em história. Quando a taxa de erro sobe, você pode alinhar o pico com um deploy e restringir a busca ao código que mudou.

Exemplo: erros de signup aumentam após o build web@3f2c1a9. Os breadcrumbs mostram “clicou em Sign up”, “POST /api/signup”, depois um 500. Você não precisa do email do usuário para agir. Precisa da release, do endpoint e do passo que falhou.

Contexto acionável sem dados pessoais

Bons relatórios de erro respondem a uma pergunta rápido: o que aconteceu imediatamente antes do crash? Dá para obter essa clareza sem copiar emails, tokens, endereços ou corpos de requisição inteiros para os breadcrumbs.

Comece adicionando algumas tags seguras que descrevem a situação, não a pessoa. Exemplos úteis são estado de feature flag (on/off), nível do tenant (free/pro/enterprise), tipo de dispositivo (mobile/desktop) e nível do plano. Essas tags transformam um monte de erros em grupos acionáveis.

Contexto de requisição é outra área de alto sinal, mas mantenha mínimo:

  • Método HTTP
  • Template de rota (por exemplo, /projects/:id/settings, não o ID real)
  • Código de status

Se adicionar latência, considere arredondar (por exemplo, 1200ms) em vez de armazenar tempos extremamente precisos por sessão do usuário.

Para breadcrumbs, pense em snapshots de estado seguros: nome da tela, número do passo em um fluxo, contador de retries. Só isso já pode mostrar padrões como “falha no passo 2 após 3 tentativas no mobile”, o que normalmente é suficiente para encontrar um bug lógico.

Um conjunto compacto de campos úteis e PII-safe:

  • screen, flow_step, retry_count
  • route_template, method, status_code
  • feature_flag, tenant_tier, plan_level, device_type
  • release, build, environment

Para conectar frontend e backend sem dados do usuário, adicione um correlation ID. Gere um ID aleatório por requisição (ou por sessão), envie-o em um header e armazene como tag ou extra em ambos os lados. Quando breadcrumbs mostrarem uma requisição com falha, você pode encontrá-la no servidor usando esse mesmo ID.

Casos especiais: auth, pagamentos e protótipos gerados por IA

Algumas partes de um app têm maior probabilidade de vazar dados sensíveis. Se quiser breadcrumbs PII-safe, trate auth e pagamentos como “sempre perigosos” e proteja-os primeiro.

No servidor, o padrão mais seguro é capturar menos. Remova headers de requisição que não precisa, redija corpos por padrão e nunca reencaminhe cookies crus para eventos de erro. Se capturar um body temporariamente para debug, permita apenas chaves específicas e limite o tamanho.

Para fluxos de autenticação, redija tudo que possa ser usado para autenticar como o usuário:

  • Headers Authorization (Bearer tokens)
  • Cookies e IDs de sessão
  • JWTs, refresh tokens e tokens CSRF
  • Códigos OAuth, valores de state e URLs de redirect com params
  • Magic links e códigos one-time password

No cliente, tome cuidado com recursos “úteis” que coletam demais. Evite capturar snapshots completos do DOM, inputs de formulários ou conteúdo da área de transferência. Prefira breadcrumbs que descrevam a intenção sem copiar dados, como “Clicou em Sign in” ou “Validation failed: password too short”.

Pagamentos exigem a mesma abordagem. Nunca registre números completos de cartão, CVC, dados bancários ou endereços de cobrança. Se precisar de contexto, capture resultados de alto nível como “Payment provider retornou declined” junto com um código de erro do provedor.

Protótipos gerados por IA representam um risco especial porque frequentemente logam objetos inteiros “só para ver o que tem dentro”, incluindo headers e variáveis de ambiente. Se herdou código de ferramentas como Cursor, Replit, Bolt, Lovable ou v0, procure por console logs e handlers de erro que despejem requisições inteiras.

Uma regra confiável: registre ações e resultados, não payloads e segredos.

Como testar se os sanitizadores realmente funcionam

Harden auth error reporting
Verificamos fluxos de autenticação e cadastro para vazamentos de tokens, exposição de cookies e cabeçalhos inseguros.

Não presuma que os sanitizadores funcionam só porque foram configurados uma vez. Trate-os como recurso de segurança: teste em todo ambiente (local, staging, produção) após qualquer mudança de logging.

Envie um erro controlado que inclua valores canário que você nunca usaria na vida real. Você ainda deve conseguir debugar o fluxo, enquanto todos os valores sensíveis são removidos ou substituídos.

Uma rotina de teste repetível:

  • Dispare uma exceção de teste que inclua um email falso ([email protected]), um token falso (tok_test_SHOULD_NOT_LEAK) e uma string parecida com cartão (4242 4242 4242 4242).
  • Reproduza o erro em cada ambiente e confirme que o evento chegou.
  • Abra o payload completo do evento e verifique message, breadcrumbs, headers de requisição e contexto extra para redacções.
  • Pesquise eventos pelos seus valores canário. Não deve haver correspondências.
  • Repita após alterar código de autenticação, formulários, pagamentos ou analytics.

Verifique também o que seu framework adiciona automaticamente. Muitos vazamentos vêm de headers (Authorization, Cookie), URLs (query params) e corpos de formulários.

Escreva um runbook curto para quando algo escapar:

  1. pare de enviar o campo (desative o breadcrumb ou contexto)
  2. aperfeiçoe os sanitizadores ou adicione uma allowlist
  3. delete eventos impactados conforme sua política
  4. reteste com canários
  5. documente a causa raiz para que não volte

Erros comuns que causam vazamentos acidentais

A maioria dos vazamentos de privacidade não vem de um grande erro único. Vem de pequenas configurações que parecem inofensivas até um incidente aparecer na sua ferramenta de erro.

Confiar em blacklist é uma armadilha comum. Você redige password e token, então alguém adiciona ssn, dob ou inviteCode depois e isso sai sem proteção. Configurações mais seguras começam com allowlist: envie apenas os campos que realmente precisa para debugar.

Logar URLs completas é outro vazamento fácil. Query params frequentemente carregam emails, telefones, tokens de reset e IDs internos. Um breadcrumb como GET /reset?email=...&token=... pode expor exatamente o que um atacante quer. Prefira templates de rota e descarte query strings por padrão.

Corpos de requisição e resposta são onde vivem as piores surpresas. Se seu SDK captura bodies por padrão, você pode acabar enviando formulários de cadastro, payloads de auth, objetos de pagamento, prompts de usuário e trechos de conteúdo enviado.

Cuidado também com o objeto user. Usar email ou telefone cru como user.id faz com que todo evento seja diretamente identificável. Use um ID interno estável ou um hash unidirecional e mantenha campos pessoais atrás de opt-in explícito e sanitização.

Checklist rápido antes do deploy

Find what Sentry is collecting
Identificamos onde o Sentry está capturando emails, parâmetros de query ou corpos de requisição.

Faça uma checagem rápida com a mentalidade “o que isso pode revelar?”. Relatórios de erro devem ajudar a consertar bugs, não virar um banco de dados sombra de informações pessoais.

Um conjunto curto de verificações evita a maioria dos vazamentos:

  • Mantenha breadcrumbs estruturados e monótonos: campos fixos como category, action, status e IDs internos. Evite texto livre vindo do usuário (termos de busca, campos de formulário, mensagens de chat), mesmo temporariamente.
  • Redija segredos onde quer que estejam: bloqueie ou redija headers Authorization, cookies, IDs de sessão, tokens CSRF, campos password, chaves de API e valores que batem com padrões de token.
  • Faça cada evento acionável: confirme que release e environment estão anexados a cada erro.
  • Template rotas e controle URLs: registre /users/:id/settings em vez de /users/48392/settings. Descarte query strings por padrão.
  • Prove a redacção de ponta a ponta: envie um evento de teste com segredos falsos (como Bearer test_token_123) e um email falso, depois verifique no painel se aparece [REDACTED] ou nada.

Antes do lançamento, escolha um fluxo realista (cadastro ou checkout), provoque um erro controlado e confirme que o relatório ainda tem contexto suficiente (template de rota, release, estado de feature flag, status de rede) para debugar sem expor usuários.

Exemplo: debugar um cadastro quebrado sem expor dados

Uma história comum: você faz deploy numa sexta e falhas de cadastro disparam. Usuários veem um vago “Algo deu errado” depois de clicarem em “Criar conta”. Você precisa de contexto suficiente para consertar rápido, mas não pode vazar emails, tokens ou headers de autenticação para a ferramenta de erro.

Com breadcrumbs PII-safe no Sentry, o relatório ainda pode ser acionável. O evento mostra o caminho sem o payload privado:

  • Breadcrumbs: Signup screen opened -> Email form submitted -> POST /api/signup (400) -> Magic link screen shown -> POST /api/verify (401)
  • Contexto: environment production, navegador e SO, estado de feature flag (on/off) e códigos de resposta da API
  • Tags: flow=signup, provider=email_magic_link, region=us-east

Ao mesmo tempo, os sanitizadores redigem valores sensíveis antes de qualquer coisa deixar o app. O evento deve substituir ou remover:

  • Valores do input de email e campos de “nome”
  • Token de magic link, código OTP, cookies de sessão
  • Header Authorization e qualquer x-api-key

Agora o porquê fica claro sem dados pessoais: erros começaram com a release [email protected], e a maioria acontece em POST /api/verify com um 401. Isso aponta para uma mudança de lógica ou configuração, não comportamento aleatório do usuário.

O rastreamento de release reduz ainda mais a busca. Compare commits entre 1.8.2 e 1.8.3 e geralmente você encontra uma pequena mudança como um endpoint renomeado, um cookie faltando ou um middleware novo bloqueando a rota de verify.

Próximos passos: manter seguro conforme o app cresce

Quando breadcrumbs PII-safe estiverem funcionando, trate privacidade como um trabalho contínuo. Apps mudam rápido: novos endpoints, SDKs de terceiros e logs de debug adicionados numa correção à noite.

Designe um dono único para reporting de erro seguro. Ele não precisa fazer tudo, mas deve rodar uma revisão rápida periodicamente e garantir que mudanças não enfraqueçam sanitizadores ou allowlists.

Adicione uma porta (release gate) para pegar erros óbvios antes do deploy. Mesmo um check simples no CI que procura padrões comuns de segredos pode evitar muita exposição acidental. Combine isso com uma regra de revisão de código: nada de logar corpos de requisição, headers de auth ou objetos de usuário completos.

Bons hábitos contínuos:

  • Revise eventos recentes de erro e confirme que a sanitização ainda funciona
  • Reavalie a allowlist ao adicionar novos breadcrumbs ou integrações de SDK
  • Rode rotação de chaves se algo sensível foi capturado, e então aperfeiçoe filtros
  • Faça auditoria direcionada após grandes features (mudanças de auth, billing, uploads)
  • Mantenha uma norma de equipe: debugar com IDs e contagens, não dados brutos

Se herdou um projeto gerado por IA que é ruidoso, quebrado ou vaza segredos, uma auditoria focada pode ser mais rápida do que tentar adivinhar onde os logs acontecem. FixMyMess (fixmymess.ai) se especializa em diagnosticar e reparar codebases gerados por IA e endurecê-los para produção, incluindo logging e reporting de erro mais seguros.