Voltar para o blog
BunPrivacidadeSegurançaBackend

Vigilância do ICE: a lição de dados para quem usa Bun

29 de outubro de 2026·6 min de leitura·Diego Horvatti

A Wired publicou uma reportagem dizendo que o ICE, a agência de imigração dos Estados Unidos, está tocando uma das maiores operações de vigilância contra movimentos de esquerda da história do país. Esse caso de vigilância do ICE parece assunto de política, não de código. Só que toda operação desse tamanho depende de software. Alguém escreveu o endpoint que guarda o IP. Alguém decidiu que o log ia durar para sempre. E muito provavelmente esse alguém nem sabia para que aquilo seria usado depois.

A matéria completa está aqui: ICE Is Running One of the Largest Anti-Left Surveillance Operations in US History. Vale a leitura. Aqui eu quero falar da parte que cabe a nós, que escrevemos backend todo dia.

O que a reportagem mostra

Não vou recontar a matéria inteira, e você deve ler a fonte antes de tirar conclusões. O ponto central é simples: uma agência do governo juntou informação sobre pessoas e grupos por causa da posição política deles. Essa informação vem de algum lugar. Redes sociais, bases comerciais, registros públicos, cruzamento de dados.

Nenhum desses bancos nasceu para vigiar ativista. Nasceram para vender anúncio, prevenir fraude, medir engajamento. Viraram ferramenta de vigilância porque estavam lá, cheios, prontos para consulta.

Esse é o padrão que se repete em quase todo escândalo de dados. O vilão raramente é um sistema projetado para o mal. É um sistema comum que guardou demais e por tempo demais.

Por que isso importa para quem escreve código

Porque o dado que você não coleta não pode ser pedido por ninguém. Nem por um governo, nem por um invasor, nem por um estagiário curioso com acesso ao banco de produção.

Pensa no último projeto que você subiu. Provavelmente tem:

  • Log de request com IP, user agent e corpo da requisição
  • Tabela de usuários com telefone, endereço e data de nascimento "porque vai que precisa"
  • Analytics guardando evento por usuário desde o primeiro dia
  • Backup do banco sem prazo de validade

Nada disso é maldade. É preguiça mal direcionada. E eu falo como alguém que já fez tudo isso.

Dado guardado sem motivo é um passivo esperando um dono.

O ponto que pouca gente encara: a decisão de reter é técnica, mas a consequência é política. Você não controla quem vai pedir esses dados daqui a cinco anos. Controla só o que existe para ser pedido.

Como reduzir a coleta num backend Bun

Agora a parte prática. Uso Bun na maioria dos meus projetos de API, então os exemplos vão nele. A lógica vale para Node, Deno ou qualquer coisa.

O primeiro passo é o mais chato: olhar o que você loga. Um logger de request ingênuo costuma ser assim:

Bun.serve({
  async fetch(req, server) {
    const body = await req.clone().text();
    console.log({
      ip: server.requestIP(req)?.address,
      ua: req.headers.get("user-agent"),
      url: req.url,
      body,
    });
    return handle(req);
  },
});

Isso salva IP, navegador, URL com query string (que às vezes carrega e-mail ou token) e o corpo inteiro. Se alguém manda senha num POST, ela vai parar no log. Já vi isso em produção mais de uma vez.

Uma versão mais honesta loga só o que ajuda a depurar:

Bun.serve({
  async fetch(req) {
    const { pathname } = new URL(req.url);
    const start = performance.now();
    const res = await handle(req);
    console.log({
      method: req.method,
      path: pathname,
      status: res.status,
      ms: Math.round(performance.now() - start),
    });
    return res;
  },
});

Sem IP, sem corpo, sem query string. Na prática, quase nunca senti falta. Quando um bug precisa de mais contexto, eu ligo um log detalhado temporário e desligo depois.

E quando eu preciso identificar o usuário?

Às vezes precisa mesmo. Rate limit por IP é o exemplo clássico. A saída é guardar algo que sirva para comparar, mas não para identificar.

O Bun tem hash nativo, sem dependência nenhuma:

const salt = Bun.env.IP_SALT!; // trocar periodicamente

function ipKey(ip: string) {
  return new Bun.CryptoHasher("sha256").update(salt + ip).digest("hex");
}

Com um salt que muda, por exemplo, todo dia, você consegue limitar requisições sem montar um histórico de quem acessou o quê. Quando o salt troca, o hash antigo vira lixo inútil. Um aviso honesto: espaço de IPv4 é pequeno, então hash com salt fixo e vazado é quebrável por força bruta. É a rotação que faz o trabalho de verdade.

Para senha, nem precisa pensar. Use Bun.password.hash, que já vem com argon2 por padrão. Se você ainda está salvando senha com SHA puro em 2026, esse artigo é para você em dobro.

Quanto tempo devo guardar os dados?

A resposta curta: o mínimo que a funcionalidade exige, com prazo escrito no código. A LGPD já pede isso, com o princípio de necessidade. A maioria dos projetos ignora porque ninguém cobra.

Eu gosto de deixar a retenção como regra do banco, não como promessa no README. Com bun:sqlite fica assim:

import { Database } from "bun:sqlite";

const db = new Database("app.db");

// eventos de analytics vivem 30 dias
db.run(`DELETE FROM events WHERE created_at < datetime('now', '-30 days')`);

No PostgreSQL é a mesma ideia, rodando num job agendado. O importante é que a limpeza seja automática. Retenção que depende de alguém lembrar não é retenção, é intenção.

Algumas regras que adotei e recomendo:

  • Logs de aplicação: 7 a 14 dias
  • Eventos de analytics por usuário: agregue e apague o bruto em 30 dias
  • Conta excluída: apague de verdade, não só deleted_at
  • Backups: com prazo, e testados (backup eterno é só um banco paralelo sem controle de acesso)

O soft delete merece um comentário. Ele é ótimo para o botão "desfazer". É péssimo como política de privacidade. Se o usuário pediu para sair, o registro com deleted_at preenchido continua respondendo a qualquer consulta que ignore essa coluna. Inclusive uma consulta feita por ordem judicial.

A objeção de sempre: "mas e se eu precisar depois?"

Eu ouço isso em toda reunião de produto. E entendo. Dado parece de graça, e jogar fora parece desperdício.

Mas faça a conta inteira. Cada campo guardado tem custo de armazenamento, de backup, de conformidade, de resposta a incidente. E tem o custo que não aparece na fatura da AWS: o risco de aquilo ser usado contra a pessoa que confiou no seu sistema.

Na minha experiência, o "vai que precisa" quase nunca chega. Quando chega, você começa a coletar a partir dali e pronto. Perder três meses de histórico raramente quebra um produto. Vazar três anos de histórico pode quebrar a empresa.

E sim, eu sei que o meu app de lista de tarefas não é alvo do governo americano. O seu provavelmente também não. Mas os bancos que aparecem nesse tipo de reportagem também não se achavam alvo. Eram só startups com um SELECT * generoso.

Minha opinião

Não acho que dev seja culpado por cada abuso feito com dado. A responsabilidade maior é de quem decide usar. Mas acho que a gente subestima o poder que tem no momento de escrever o schema. Uma coluna a menos, um log mais curto, um job de limpeza rodando toda madrugada. São decisões pequenas, que cabem num PR de 20 linhas, e que mudam o que existe para ser vigiado.

O Bun facilita isso porque já traz hash, senha e SQLite sem pacote extra. Não tem mais desculpa de "dá trabalho configurar". O que falta é o hábito de perguntar, antes de cada campo novo, se ele precisa mesmo existir.

Se quiser ver como eu aplico isso nos meus próprios sistemas, dá uma olhada nos projetos que mantenho.

Resumo para o LinkedIn

O dado que você não coleta ninguém pode pedir. Nem governo, nem invasor, nem estagiário curioso.

A Wired mostrou que o ICE montou uma das maiores operações de vigilância política da história dos EUA. Nenhum dos bancos usados nasceu para vigiar alguém. Nasceram para vender anúncio e medir engajamento.

O vilão quase nunca é um sistema feito para o mal. É um sistema comum que guardou demais, por tempo demais.

Log com IP e corpo da requisição, telefone "porque vai que precisa", backup sem prazo. Eu já fiz tudo isso.

No Bun dá para resolver com pouco: log sem dado pessoal, hash de IP com salt rotativo, Bun.password para senha e um DELETE agendado para a retenção. Um PR de 20 linhas muda o que existe para ser vigiado.

Escrevi o passo a passo com código no blog. Antes de criar o próximo campo, vale perguntar se ele precisa existir.

#Privacidade #LGPD #Bun #Backend #DesenvolvimentoDeSoftware