Renan Montanari Blog
Voltar ao blog
SegurançaArquitetura

Controle de Acesso: Quando Todo Mundo Consegue Ver e Editar Tudo

No começo, todo mundo é admin. Anos depois, ninguém sabe quem ainda tem acesso nem quem alterou o quê. Papéis, permissões e auditoria: o que dá para resolver em uma tarde.

Renan Montanari

Renan Montanari

Engenheiro de Software Full-Stack

10 min de leitura

Introdução: O Teste Mental Incômodo

Faça um teste mental rápido.

Liste quem hoje consegue entrar no seu sistema e ver tudo:

  • valores, dados de clientes, histórico de vendas;
  • relatórios de faturamento;
  • informações de parceiros;
  • tudo.

Agora conte quantas dessas pessoas ainda trabalham com você.

Em quase todo negócio pequeno, a resposta é incômoda.

E não é descuido. Não é negligência. É o caminho natural das coisas.

Por Que Ninguém Faz Controle de Acesso no Começo

O Cenário de Sempre

Você começa com 3 pessoas: você, seu sócio e um dev. Todos fazem tudo.

Separar permissão parece burocracia de empresa grande. O sistema nasce com um nível de acesso só: acesso total. E funciona bem assim. Todos confiam uns nos outros. Todos sabem exatamente o que o outro está fazendo.

Aí:

  • o time cresce para 5 pessoas;
  • alguém sai, mas continua com o login e as senhas;
  • entra um estagiário que precisa ver só os pedidos;
  • você contrata uma agência externa que precisa acessar a integração com o gateway de pagamento, mas não precisa ver mais nada;
  • um freelancer entra por 3 meses e depois sai.

E ninguém nunca volta no sistema para "arrumar as permissões". Porque:

  1. Parece que funciona: tudo continua de pé.
  2. Ninguém avisa que é urgente: até dar problema.
  3. É um problema chato: pior que corrigir um bug.
  4. Você não vê o risco até ele virar realidade.

O Risco Que Todo Mundo Ignora

Não É Má-Fé, É Acidente

O risco comum não é alguém agir de má-fé. Não é espionagem corporativa. É bem mais banal:

gente apagando ou alterando coisa sem querer, porque tinha permissão para isso.

Exemplos que acontecem toda semana em algum lugar:

  • Estágio encerrado: o estagiário continua com acesso 6 meses depois.
  • Pedido editado por engano: alguém mexe num campo "só para testar".
  • Preço alterado sem querer: a pessoa digita na linha errada e salva sem ninguém revisar.
  • Cliente excluído da base: um teste feito com dados reais que nunca foi desfeito.
  • Faturamento exposto por engano: alguém abriu a tela errada na reunião com o fornecedor.
  • Backup perdido: alguém com acesso total apagou "para limpar" e ninguém tinha cópia.

E a pior parte? Ninguém fica sabendo até ser tarde.

A Pergunta Que Ninguém Consegue Responder

Quando algo dá errado e todos têm o mesmo acesso, aparece a pergunta:

"Quem fez isso? E quando?"

E a resposta é: você não sabe.

Porque não existe como reconstruir o que aconteceu.

Não se trata de achar culpado. Não se trata de desconfiar das pessoas.

Trata-se de conseguir ver o que mudou e voltar atrás.

Permissão Bem Feita Cabe em uma Tarde

Permissão bem feita não é desconfiar das pessoas.

É cada um conseguir fazer o próprio trabalho sem conseguir, por acidente, desfazer o trabalho dos outros.

E duas coisas cabem numa tarde:

  1. Listar quem tem acesso hoje e remover quem não deveria mais ter.
  2. Perguntar para quem cuida do seu sistema se é possível saber quem alterou o quê, e quando.

Vamos às duas.

1️⃣ Controle de Acesso por Papel (RBAC)

O Problema: Todo Mundo É Admin

// ❌ Amador: estar logado já basta para fazer qualquer coisa
const isUserAllowed = (user: User) => {
  return user.exists;
};

export const API = {
  viewCustomers: async (user: User) => {
    if (!isUserAllowed(user)) throw new Error("Denied");
    return db.customers.findAll();
  },
  editPrice: async (user: User, productId: string, newPrice: number) => {
    if (!isUserAllowed(user)) throw new Error("Denied");
    return db.products.update(productId, { price: newPrice });
  },
  deleteCustomer: async (user: User, customerId: string) => {
    if (!isUserAllowed(user)) throw new Error("Denied");
    return db.customers.delete(customerId);
  },
};

Problema: um estagiário consegue apagar cliente. Uma agência externa consegue ver o histórico de preços.

A Solução: Papéis e Permissões

// ✅ Profissional: RBAC (Role-Based Access Control)

enum Role {
  ADMIN = "admin", // tudo
  MANAGER = "manager", // vê dados, edita clientes
  SALES = "sales", // cria pedidos, vê clientes
  INTERN = "intern", // só visualiza clientes
  EXTERNAL_AGENCY = "external_agency", // só a integração com o gateway
}

// Permissões por papel
const permissions: Record<Role, string[]> = {
  [Role.ADMIN]: [
    "view:customers",
    "edit:customers",
    "delete:customers",
    "view:prices",
    "edit:prices",
    "view:sales",
    "manage:users",
    "view:audit_logs",
  ],
  [Role.MANAGER]: [
    "view:customers",
    "edit:customers", // não pode apagar
    "view:prices",
    "view:sales",
    "view:audit_logs",
  ],
  [Role.SALES]: [
    "view:customers",
    "create:orders",
    "view:orders", // "só os próprios" exige checar o dono do pedido na query
  ],
  [Role.INTERN]: [
    "view:customers", // só leitura
  ],
  [Role.EXTERNAL_AGENCY]: [
    "webhook:gateway_integration", // nada mais
  ],
};

// Uma única regra, usada em todo lugar
const can = (user: User, permission: string) =>
  permissions[user.role]?.includes(permission) ?? false;

const assertCan = (user: User, permission: string, message: string) => {
  if (!can(user, permission)) throw new ForbiddenError(message);
};

// Middleware para as rotas HTTP
const authorize = (permission: string) => {
  return (req: Request, res: Response, next: NextFunction) => {
    if (!can(req.user, permission)) {
      return res.status(403).json({ error: "Acesso negado" });
    }
    next();
  };
};

// E na camada de serviço, para qualquer caminho que chegue até aqui
export const API = {
  viewCustomers: async (user: User) => {
    assertCan(user, "view:customers", "Você não tem permissão para ver clientes");
    return db.customers.findAll();
  },

  editPrice: async (user: User, productId: string, newPrice: number) => {
    assertCan(user, "edit:prices", "Apenas admins podem editar preços");
    return db.products.update(productId, { price: newPrice });
  },

  deleteCustomer: async (user: User, customerId: string) => {
    assertCan(user, "delete:customers", "Apenas admins podem apagar clientes");
    return db.customers.delete(customerId);
  },
};

Mapeie Seus Papéis Reais

Antes de escrever código, escreva a lista. Um exemplo:

  1. ADMIN (você e seu sócio): tudo.
  2. MANAGER (gerente de operações): vê dados, edita clientes, gera relatórios, vê os logs de auditoria. Não pode apagar, editar preços ou gerenciar usuários.
  3. SALES (time de vendas): vê clientes, cria pedidos, vê apenas os pedidos que criou. Não pode ver dados financeiros nem editar clientes.
  4. SUPPORT (suporte ao cliente): vê o cliente e os pedidos dele, adiciona notas. Não pode editar dados sensíveis nem ver histórico de preço.
  5. EXTERNAL_GATEWAY_PARTNER (parceiro de pagamento): acesso apenas ao webhook da integração.
  6. CONTRACTOR (freelancer ou agência): acesso limitado a dados específicos, com data para expirar automaticamente.

2️⃣ Auditoria: Saber Quem Fez o Quê

O Problema: Nenhuma Trilha

Alguém editou um preço. Alguém apagou um cliente. Alguém alterou uma data.

Você não sabe quem. Não sabe quando. Não consegue reverter.

A Solução: Logs de Auditoria

O registro precisa do valor antigo e do valor novo. Por isso ele é feito onde a alteração acontece, na mesma transação: se a alteração for salva, o log também é; se falhar, nenhum dos dois fica.

// ✅ Profissional: registre tudo que importa

interface AuditLog {
  id: string;
  timestamp: Date;
  userId: string;
  userEmail: string;
  action: "create" | "update" | "delete";
  resource: "customer" | "order" | "product";
  resourceId: string;
  changes: {
    field: string;
    oldValue: unknown;
    newValue: unknown;
  }[];
  ipAddress?: string;
  userAgent?: string;
}

const updatePrice = async (user: User, productId: string, newPrice: number) => {
  assertCan(user, "edit:prices", "Apenas admins podem editar preços");

  return db.transaction(async (tx) => {
    const product = await tx.products.findById(productId);

    await tx.products.update(productId, { price: newPrice });

    await tx.auditLogs.create({
      id: generateId(),
      timestamp: new Date(),
      userId: user.id,
      userEmail: user.email,
      action: "update",
      resource: "product",
      resourceId: productId,
      changes: [{ field: "price", oldValue: product.price, newValue: newPrice }],
    });
  });
};

// Na rota: autentica, autoriza e chama o serviço
app.put(
  "/products/:id/price",
  authenticate,
  authorize("edit:prices"),
  (req, res) => updatePrice(req.user, req.params.id, req.body.price),
);

Consultar os Logs de Auditoria

// "O que aconteceu com esse cliente?"
const getCustomerHistory = async (customerId: string) => {
  return db.auditLogs.find({
    where: { resource: "customer", resourceId: customerId },
    orderBy: { timestamp: "desc" },
  });
};

// Resultado:
// [
//   {
//     timestamp: "2026-10-05 14:32",
//     userEmail: "joao@empresa.com",
//     action: "update",
//     changes: [{ field: "phone", oldValue: "11987654321", newValue: "11987654322" }],
//   },
//   {
//     timestamp: "2026-10-04 10:15",
//     userEmail: "maria@empresa.com",
//     action: "update",
//     changes: [{ field: "status", oldValue: "active", newValue: "inactive" }],
//   },
// ]

// "Quem alterou o quê nesse período?"
const whoEditedWhat = async (startDate: Date, endDate: Date) => {
  return db.auditLogs.find({
    where: { action: "update", timestamp: { gte: startDate, lte: endDate } },
  });
};

// "Essa pessoa apagou alguma coisa?"
const checkDeletions = async (userId: string) => {
  return db.auditLogs.find({ where: { userId, action: "delete" } });
};

A Tabela de Auditoria (PostgreSQL)

CREATE TABLE audit_logs (
  id          UUID PRIMARY KEY,
  timestamp   TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  user_id     UUID NOT NULL,          -- sem FK: o log precisa sobreviver à remoção do usuário
  user_email  VARCHAR(255) NOT NULL,
  action      VARCHAR(20) NOT NULL,   -- 'create', 'update', 'delete'
  resource    VARCHAR(50) NOT NULL,   -- 'customer', 'order', 'product'
  resource_id UUID NOT NULL,
  changes     JSONB NOT NULL,         -- [{ field, oldValue, newValue }]
  ip_address  VARCHAR(45),
  user_agent  TEXT
);

CREATE INDEX idx_audit_user_timestamp ON audit_logs (user_id, timestamp DESC);
CREATE INDEX idx_audit_resource ON audit_logs (resource, resource_id);
CREATE INDEX idx_audit_action ON audit_logs (action);

-- Histórico de um registro
SELECT * FROM audit_logs
WHERE resource = 'customer' AND resource_id = '6f1c2a90-0000-0000-0000-000000000123'
ORDER BY timestamp DESC;

-- Quem mais apagou coisas nos últimos 7 dias
SELECT user_email, COUNT(*) AS deletions
FROM audit_logs
WHERE action = 'delete' AND timestamp > NOW() - INTERVAL '7 days'
GROUP BY user_email
ORDER BY deletions DESC;

Checklist: Quando Alguém Entra ou Sai

Quando contrata:

  • Definiu qual papel essa pessoa vai ter?
  • Criou uma conta própria para ela, sem compartilhar login?
  • Registrou quem tem acesso a quê?
  • Ativou a verificação em duas etapas (2FA) nessa conta?
  • Avisou o time que essa pessoa tem acesso?

Quando a pessoa sai:

  • Removeu o acesso no mesmo dia?
  • Trocou as senhas compartilhadas?
  • Revogou os tokens de API?
  • Conferiu se houve alguma alteração suspeita antes da saída?
  • Registrou a saída no log de auditoria?

Hoje mesmo:

  • Tem a lista de quem tem acesso atualmente?
  • Sabe quantas dessas pessoas ainda trabalham com você?
  • Removeu os acessos que não fazem mais sentido?
  • Tem registro de quem alterou o quê?
  • Consegue responder "o que mudou ontem"?

Implementação Prática: Uma Tarde

Hora 1: logs de auditoria

  • Criar a tabela no banco
  • Registrar as alterações nos fluxos críticos (preço, cliente, pedido)

Hora 2: RBAC

  • Definir seus 3 a 5 papéis principais
  • Mapear as permissões por papel
  • Atualizar o código de autorização

Hora 3: limpeza

  • Listar quem tem acesso hoje
  • Remover quem não deveria mais ter
  • Atribuir os papéis corretos

Hora 4: painel de auditoria

  • Criar uma página simples que mostre:
    • quem fez o quê;
    • quando foi feito;
    • o histórico de mudanças por registro.

O Medo Que Ninguém Comenta

Quando você implementa isso, aparece uma pergunta incômoda:

"E se eu descobrir que alguém fez algo errado?"

Essa é a verdadeira razão pela qual muita gente evita auditoria.

Mas o ponto é este: você descobriria de qualquer jeito na hora de investigar um problema. A diferença é que agora você fica sabendo antes e pode corrigir antes de virar crise.

Além disso, na imensa maioria das vezes não é "alguém fez errado". É "ops, fiz sem querer".

Auditoria não é sobre culpa. É sobre saber o que acontece na sua operação.

Próximos Passos

Se alguma destas frases descreve o seu sistema:

  • "Não sei quem tem acesso ao meu sistema."
  • "Não consigo reverter uma alteração."
  • "Alguém apagou algo e não sei quem foi."
  • "Todo mundo entra como admin."
  • "Alguém saiu e ainda tem acesso."

Então você precisa disso.

Uma tarde implementando controle de acesso e auditoria economiza semanas de investigação.

E, mais importante, poupa você do susto de descobrir que seus dados foram alterados sem deixar rastro.

Precisa de Ajuda Com Controle de Acesso?

Se o seu sistema está crescendo e você quer garantir que os dados sensíveis estão protegidos, não de hackers, mas de acidentes, posso ajudar com:

  • Auditoria de acessos: quem tem acesso a quê e onde estão os riscos
  • RBAC implementado: papéis e permissões prontos para produção
  • Auditoria automática: registro de tudo que importa e um painel para investigar
  • Segurança na arquitetura: um design que protege sem burocratizar

Vamos revisar o controle de acesso do seu sistema →

Gostou? Compartilhe