Autorização (RBAC e ABAC)
Modelos para decidir o que cada usuário pode fazer, por papel ou por atributos.
As decisões de autorização são quatro: qual modelo usar, onde fazer a conferência, onde a política mora e como isolar os dados de cada empresa.
Qual modelo
| RBAC (papel) | ABAC (atributos) | ReBAC (relação) | |
|---|---|---|---|
| A regra diz | "O financeiro pode reembolsar" | "Reembolsa quem é do financeiro, se o pedido for de até R$ 1.000" | "Edita quem é membro da equipe dona da pasta" |
| Pergunta que responde bem | Quem pode fazer o quê, em geral | Pode fazer isto, neste recurso, agora? | Quem tem acesso a este item, e por quê? |
| Auditoria | Fácil: uma tabela | Difícil: depende dos dados de cada recurso | Média: segue o caminho das relações |
| Custo | Baixo | Médio | Alto: um serviço e uma base de relações |
| Bom para | Painéis administrativos, poucos papéis | Regras de negócio sobre o recurso: dono, valor, região, horário | Compartilhamento: documentos, pastas, equipes, organizações |
A maioria dos sistemas de negócio fica bem com RBAC mais atributos do recurso: o papel define o alcance de cada permissão (os próprios, os da equipe, todos), e um atributo do recurso, quase sempre o dono, decide o caso. O ReBAC compensa quando os usuários compartilham itens entre si e a pergunta "quem pode ver isto?" precisa de resposta rápida.
Cuidado com a explosão de papéis
Um sinal de que o RBAC puro não basta: papéis como gerente_sp, gerente_rj, gerente_sp_leitura. Cada combinação de regra com atributo virou um papel. A saída é tirar o atributo do nome do papel e colocá-lo na regra: o papel gerente, e a regra "na região do usuário".
Onde fazer a conferência
| Camada | O que confere bem | O que não consegue conferir |
|---|---|---|
| Gateway ou proxy | Autenticação, escopos do token, rotas inteiras ("só o financeiro chama /reembolsos") | O dono do recurso: o gateway não sabe de quem é o pedido 981 |
| Rota ou middleware | Permissões gerais por rota | O dono, antes de carregar o recurso |
| Serviço, com o recurso carregado | Tudo: o usuário, o recurso e o contexto | Nada; é aqui que a conferência por objeto acontece |
| Consulta ao banco | Filtra as listagens pelo que o usuário pode ver | Regras complexas; vira SQL difícil de manter |
| Segurança por linha no banco | Isolamento entre empresas, mesmo se uma consulta esquecer o filtro | Regras de negócio finas |
As camadas se somam. A conferência por objeto, a que evita o IDOR, só pode acontecer depois de carregar o recurso, ou dentro da consulta que o carrega. Um middleware na entrada da rota não basta.
Para as listagens, a regra vira filtro: "os pedidos que o usuário pode ler" precisa ser traduzido em WHERE cliente_id = $1 para clientes e em nenhum filtro para o suporte. Carregar tudo e filtrar na memória funciona com poucos registros e quebra a paginação.
Onde a política mora
| Opção | Como funciona | Quando usar |
|---|---|---|
| No código, numa função central | Uma função pode(usuario, acao, recurso) e uma tabela de alcances | O começo de quase todo sistema; uma aplicação só |
| Biblioteca de políticas | Casbin, CASL, Pundit, as políticas do ASP.NET Core e do Spring Security | Para organizar regras dentro de uma aplicação |
| Motor de políticas (OPA, Cedar) | A política fica num arquivo próprio, numa linguagem de regras, avaliada por um motor | Várias aplicações e linguagens com as mesmas regras; auditoria da política separada do código |
| Serviço de autorização (OpenFGA, SpiceDB, Permify) | Um serviço guarda as relações e responde "pode?" | ReBAC; compartilhamento em grande escala |
O ponto em comum das quatro: um só lugar. O que dá errado é a regra espalhada em ifs pelas rotas, cada um com uma versão um pouco diferente.
O motor de políticas tem um custo que não aparece no começo. Ele precisa dos dados do recurso para decidir, e alguém tem de buscá-los e enviá-los a cada pergunta. Para regras de dono e de papel numa aplicação só, a função central no código é mais simples e igualmente segura.
Como isolar empresas
| Abordagem | Isolamento | Custo |
|---|---|---|
| Filtro em cada consulta | Depende de nenhuma consulta esquecer | Nenhum, até o primeiro esquecimento |
| Filtro automático no ORM (escopos globais, filtros de consulta) | Bom, mas consultas em SQL puro escapam | Baixo |
| Segurança por linha no Postgres | O banco aplica, inclusive em SQL puro | Configuração cuidadosa; ver as armadilhas abaixo |
| Um esquema ou um banco por empresa | Forte | Migrações e operação multiplicadas pelo número de empresas |
A segurança por linha só protege se as armadilhas medidas no nível Conceito forem evitadas:
- A aplicação conecta com um usuário que não é dono das tabelas, nem superusuário, nem tem
BYPASSRLS. Ou a tabela usaFORCE ROW LEVEL SECURITY, que no teste fez o dono respeitar a política, mas não o superusuário. - A empresa é definida com
SET LOCAL, ouset_config(..., true), dentro da transação de cada pedido. ComSET, o valor ficou na conexão e vazou para o pedido seguinte. - A política trata o valor ausente, com
nullifsobre ocurrent_setting. - Um teste automatizado confere que uma consulta sem empresa definida devolve zero linhas.
Permissões no token ou consultadas
| No token | Consultadas a cada pedido | |
|---|---|---|
| Velocidade | Nenhuma consulta | Uma consulta, que um cache curto resolve |
| Mudança de papel | Só vale quando o token vencer | Imediata, ou depois do cache |
| Tamanho | O token cresce com as permissões | Não muda |
Uma combinação comum: o token leva o identificador e escopos amplos e estáveis; papéis e permissões finas vêm da base, com cache de segundos ou minutos.
Resumo da decisão
- RBAC com atributos do recurso: papéis que definem o alcance, e o dono decide o caso. ReBAC quando o compartilhamento é central.
- Conferência por objeto em toda rota que recebe um identificador, depois de carregar o recurso.
- Uma função ou um motor de políticas, num só lugar, negando o que não estiver permitido.
- 404 para recursos que o usuário não pode ver; 403 para ações proibidas em recursos visíveis.
- Isolamento de empresas no banco com segurança por linha, sem dono da tabela e com
SET LOCAL. - Permissões consultadas quando mudam ou dão muito poder, em vez de presas no token.
O próximo nível mostra a mesma política nas cinco linguagens e num motor de políticas, testadas com os mesmos dez pedidos, e a segurança por linha no Postgres.
Prompts para aprofundar
Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.
Desenhar a autorização do meu sistema
Quero desenhar a autorização do meu sistema. Pergunte quais são os tipos de usuário, os recursos principais, as ações sobre eles, e se há várias empresas clientes na mesma base. Depois proponha, explicando o porquê: 1. O modelo: papéis, atributos, relações ou uma mistura. 2. A tabela de permissões e o alcance de cada uma. 3. Onde cada conferência acontece: gateway, rota, serviço, consulta ou banco. 4. Como as listagens são filtradas. 5. Como as empresas ficam isoladas.
Escolher onde a política mora
Estou decidindo onde a política de autorização do meu sistema deve ficar. Pergunte quantas aplicações e linguagens precisam das mesmas regras, quão complexas elas são e se há compartilhamento entre usuários. Compare para o meu caso: 1. Uma função central no código. 2. Uma biblioteca de políticas da minha linguagem. 3. Um motor de políticas, como OPA ou Cedar. 4. Um serviço de autorização por relações, como OpenFGA ou SpiceDB. Para cada opção, explique como os dados do recurso chegam à decisão e como a política é testada.
Papéis no token ou consultados
Quero decidir se as permissões dos usuários ficam dentro do token de acesso ou são consultadas a cada pedido. Pergunte como os usuários são autenticados hoje, com que frequência os papéis mudam e qual é a validade dos tokens. Explique: 1. O que acontece quando um usuário é rebaixado e o papel está no token. 2. O custo de consultar a cada pedido, e como um cache curto ajuda. 3. O que faz sentido manter no token e o que buscar na base. 4. Como revogar o acesso de alguém imediatamente.