Voltar para o blog
SegurançaBunOpenAI

Ameaça interna: o caso da OpenAI e como se proteger com Bun

15 de outubro de 2026·7 min de leitura·Diego Horvatti

Um engenheiro da OpenAI foi preso, acusado de instalar uma escuta na sala de reunião dos executivos da empresa. A notícia saiu no SF Standard e parece roteiro de filme de espionagem. Só que o assunto de fundo é bem menos cinematográfico e muito mais comum: ameaça interna. É o risco que vem de quem já tem crachá, acesso e a senha do Wi-Fi.

Antes de seguir, um aviso honesto. Os detalhes do caso ainda estão saindo e eu não vou especular sobre a pessoa nem sobre o motivo. Prisão não é condenação. O que me interessa aqui é outra coisa: o que esse tipo de caso mostra sobre o jeito que a gente constrói sistema.

O que aconteceu, em poucas linhas

Segundo a reportagem, um funcionário da área de engenharia é suspeito de grampear uma sala usada pela liderança da empresa. Não teve ransomware, nem phishing, nem zero-day. A pessoa já estava lá dentro.

E isso numa das empresas mais visadas do planeta. A OpenAI tem pesos de modelo, roadmap e negociação de bilhões de dólares passando por aquelas salas. Se dá pra acontecer lá, dá pra acontecer na sua startup de 12 pessoas.

Por que ameaça interna é o risco que todo mundo ignora

Quando a gente fala de segurança, a cabeça vai direto para o atacante externo. Firewall, rate limit, WAF, CAPTCHA no login. Tudo apontado pra fora.

Só que o modelo mental de muito time é este: quem passou pela autenticação é confiável. E pronto.

Pensa no seu projeto agora. Quantas pessoas conseguem:

  • Rodar um SELECT direto no banco de produção?
  • Ler o .env de produção?
  • Fazer deploy sem revisão de ninguém?
  • Apagar um log sem deixar rastro?

Na maioria dos times pequenos que eu conheço, a resposta para as quatro é "todo mundo". Inclusive o estagiário que entrou mês passado. Inclusive aquele token de CI que ninguém lembra quem criou.

Não estou dizendo que seu colega vai te grampear. Estou dizendo que ameaça interna nem sempre é maldade. Às vezes é um notebook roubado, uma conta comprometida, ou alguém que rodou DELETE sem WHERE às 18h de uma sexta.

Confiança não é controle de acesso.

O que muda no código: rastro que não se apaga

A escuta física é problema de segurança predial, não é o seu trabalho. Mas o equivalente digital é: alguém com acesso legítimo fazendo coisa ilegítima. E a primeira defesa contra isso é saber quem fez o quê, e ter certeza de que esse registro não foi adulterado.

Log comum não resolve. Se a pessoa tem acesso ao banco, ela apaga a linha do log junto com o estrago. Por isso eu gosto de log de auditoria encadeado por hash. Cada registro carrega o hash do anterior. Mexeu em um, quebrou a corrente inteira.

Com Bun dá pra fazer isso sem instalar nada, usando bun:sqlite e Bun.CryptoHasher:

import { Database } from "bun:sqlite";

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

db.run(`CREATE TABLE IF NOT EXISTS audit (
  id INTEGER PRIMARY KEY,
  at TEXT NOT NULL,
  actor TEXT NOT NULL,
  action TEXT NOT NULL,
  prev TEXT NOT NULL,
  hash TEXT NOT NULL
)`);

// bloqueia UPDATE e DELETE direto no banco
db.run(`CREATE TRIGGER IF NOT EXISTS audit_no_update
  BEFORE UPDATE ON audit BEGIN SELECT RAISE(ABORT, 'append-only'); END`);
db.run(`CREATE TRIGGER IF NOT EXISTS audit_no_delete
  BEFORE DELETE ON audit BEGIN SELECT RAISE(ABORT, 'append-only'); END`);

const sha = (s: string) => new Bun.CryptoHasher("sha256").update(s).digest("hex");

export function audit(actor: string, action: string) {
  const last = db.query("SELECT hash FROM audit ORDER BY id DESC LIMIT 1")
    .get() as { hash: string } | null;
  const prev = last?.hash ?? "genesis";
  const at = new Date().toISOString();
  const hash = sha(`${prev}|${at}|${actor}|${action}`);
  db.run(
    "INSERT INTO audit (at, actor, action, prev, hash) VALUES (?, ?, ?, ?, ?)",
    [at, actor, action, prev, hash],
  );
}

export function verify() {
  let prev = "genesis";
  for (const row of db.query("SELECT * FROM audit ORDER BY id").all() as any[]) {
    if (row.prev !== prev) return { ok: false, brokenAt: row.id };
    if (sha(`${row.prev}|${row.at}|${row.actor}|${row.action}`) !== row.hash) {
      return { ok: false, brokenAt: row.id };
    }
    prev = row.hash;
  }
  return { ok: true };
}

Agora, sendo honesto sobre o limite disso. Quem tem acesso de escrita ao arquivo pode derrubar o trigger e reescrever a corrente inteira do zero. O hash não impede adulteração, ele só torna a adulteração detectável. Para fechar o buraco, mande o último hash para um lugar que a mesma pessoa não controla: um bucket com retenção travada, um canal de Slack, um e-mail diário. Se o hash de ontem não bater com a corrente de hoje, alguém mexeu.

No PostgreSQL a ideia é a mesma: uma tabela audit com REVOKE UPDATE, DELETE para o usuário da aplicação e o hash calculado no app ou num trigger.

Como plugar isso numa API com Bun

Gerar o registro tem que ser automático. Se depende de alguém lembrar de chamar audit(), ninguém chama. Um wrapper nas rotas sensíveis resolve:

import { audit } from "./audit";

type Handler = (req: Request, user: string) => Response | Promise<Response>;

const sensitive = (action: string, handler: Handler) => async (req: Request) => {
  const user = req.headers.get("x-user-id"); // troque pela sua auth de verdade
  if (!user) return new Response("unauthorized", { status: 401 });
  audit(user, `${action} ${new URL(req.url).pathname}`);
  return handler(req, user);
};

Bun.serve({
  routes: {
    "/admin/export-users": sensitive("export", () => Response.json({ ok: true })),
    "/admin/rotate-keys": sensitive("rotate", () => Response.json({ ok: true })),
  },
  fetch: () => new Response("not found", { status: 404 }),
});

Repare que o registro acontece antes da ação. Se a ação falhar no meio, você ainda sabe que alguém tentou. Tentativa de exportar a base de usuários às 3h da manhã é informação valiosa, deu certo ou não.

E sim, aquele header x-user-id é só para o exemplo caber na tela. Se você confiar num header que o cliente manda, o problema de ameaça interna vira problema de ameaça de qualquer um.

O que eu mudaria no fluxo de trabalho amanhã

Código é metade da história. A outra metade é processo, e geralmente é a parte mais barata.

Menor privilégio de verdade. Ninguém precisa de acesso de escrita em produção para trabalhar no dia a dia. Crie um usuário de leitura no Postgres para debug e deixe o de escrita só para a aplicação e para migrations.

Segredo fora do repositório e fora do Slack. Se o .env de produção já passou por uma DM, considere ele vazado. Use o gerenciador de segredos da sua plataforma e rotacione o que estiver velho.

Token com dono e com prazo. Todo token de CI ou de API precisa ter um responsável e uma data de expiração. Token eterno sem dono é porta dos fundos esperando alguém achar.

Revisão obrigatória em deploy. Branch protegida na main custa cinco minutos para configurar. Isso impede que uma pessoa sozinha, de má-fé ou só cansada, coloque qualquer coisa em produção.

Offboarding no mesmo dia. Quando alguém sai do time, o acesso sai junto. Não na semana seguinte. É impressionante quanta empresa descobre meses depois que um ex-funcionário ainda tinha acesso ao painel de admin.

Nenhum desses itens é paranoia. É higiene. O mesmo tipo de cuidado que te faz escrever teste antes de mexer no cálculo de frete.

Isso não é tratar o time como suspeito?

Essa é a objeção que eu mais escuto, e entendo. Ninguém quer trabalhar num lugar onde todo mundo é vigiado.

Só que log de auditoria não é vigilância de pessoa. É registro de ação em sistema crítico. Ele protege inclusive quem é inocente. Quando algo some do banco, o log é o que prova que não foi você. Sem ele, sobra desconfiança e dedo apontado na reunião.

E tem um detalhe meio irônico no caso da OpenAI. A acusação é justamente de alguém vigiando os outros sem autorização. O antídoto não é mais vigilância escondida, é controle transparente: todo mundo sabe que as ações sensíveis ficam registradas, e as regras valem igual para o estagiário e para o CTO.

Minha opinião

Esse caso vai render manchete por semanas por causa do nome envolvido. Mas a lição para quem escreve código é chata e prática: o perímetro não é a porta do prédio, é cada permissão que você distribui. A maioria dos sistemas que eu vejo está pronta para o hacker russo e completamente aberta para quem já tem login.

Se você só fizer uma coisa depois de ler isto, faça um inventário de quem consegue escrever em produção hoje. A lista provavelmente vai te assustar mais do que qualquer escuta na sala de reunião.

Se quiser ver como eu aplico esse tipo de cuidado em sistemas reais, dá uma olhada nos projetos que eu já entreguei.

Resumo para o LinkedIn

Um engenheiro da OpenAI foi preso, acusado de grampear a sala de reunião dos executivos.

Não teve ransomware, phishing nem zero-day. A pessoa já estava lá dentro.

A maioria dos sistemas que eu vejo está pronta para o hacker russo e completamente aberta para quem já tem login.

Quem no seu time consegue rodar um SELECT em produção, ler o .env ou fazer deploy sem revisão? Na maioria dos times pequenos, a resposta é "todo mundo".

Confiança não é controle de acesso. Log de auditoria que não dá para apagar, menor privilégio e offboarding no mesmo dia são higiene, não paranoia.

Escrevi como montar um log à prova de adulteração com Bun em poucas linhas. O link está nos comentários.

#SegurançaDaInformação #Bun #DesenvolvimentoDeSoftware #DevSecOps #OpenAI