De SQL Server para PostgreSQL #03 – Login, user, role: quem manda em quê, e por que não existe um “sa” no Postgres.
SQL Server separa login e user em duas camadas; o Postgres funde tudo em um único conceito, a role — e essa fusão muda a forma como você pensa superusuário, permissão padrão e escopo de acesso.
Francisco Porfírio e Renato Siqueira · Time PostgreSQL da Power Tuning
Nos dois primeiros posts desta série, tratamos da instalação e da hierarquia instância/database/schema. Resolvidos esses dois estranhamentos, o terceiro geralmente aparece na primeira reunião de segurança: alguém pergunta “quem é o sa do Postgres?” — e a resposta correta não é um nome, é uma mudança de modelo mental.
No SQL Server, o controle de acesso é construído em duas camadas distintas: login (autenticação, nível de instância) e user (autorização, nível de database), com um mapeamento explícito entre os dois. No Postgres, essas duas camadas colapsam em um único objeto: a role. Não existe separação estrutural entre “quem se conecta” e “quem tem permissão dentro do banco”.
Este post cobre essa diferença de raiz, compara o superusuário de cada banco (sa x postgres), e explica o que vem liberado por padrão em cada instalação — incluindo uma mudança recente do Postgres que pegou muita gente de surpresa.
|
SQL Server (2 camadas) Nível instância — LOGIN
sa (sysadmin) LoginApp, LoginETL… Autentica no servidor ↓ mapeamento Nível database — USER
dbo, user_app (por database) db_owner, db_datareader… Autoriza dentro de UM database 1 login pode virar N users um mapeamento por database |
PostgreSQL (1 camada) ROLE (nível cluster, único)
postgres (superuser) app_user (LOGIN) grupo_leitura (NOLOGIN) Mesmo objeto autentica E autoriza visível em todo o cluster ↓ GRANT grupo_leitura TO app_user app_user herda de
grupo_leitura 1 role, N databases permissão é dada database a database |
No SQL Server, login (autentica) e user (autoriza) são objetos diferentes, ligados por um mapeamento explícito por database.
No Postgres, a role faz as duas coisas ao mesmo tempo: existe uma vez no cluster e recebe permissões banco a banco.
Como o SQL Server te acostumou a pensar
No SQL Server, autenticação e autorização vivem em camadas separadas. Um login existe no nível da instância — pode ser autenticação do Windows ou autenticação SQL — e por si só não dá acesso a nenhum dado. Para acessar um database específico, esse login precisa estar mapeado a um user dentro daquele database, e é o user que recebe permissões sobre schemas, tabelas e procedures.
Em cima disso, o SQL Server oferece papéis prontos em dois níveis: fixed server roles (como sysadmin, dbcreator, securityadmin) e fixed database roles (db_owner, db_datareader, db_datawriter). No topo de tudo isso está o login sa, criado automaticamente na instalação, membro fixo e permanente do papel sysadmin.
|
1 2 3 4 5 6 |
<span style="color: #8aa9ce;">-- Criar login (nível instância) e mapear para user (nível database)</span> CREATE LOGIN login_app WITH PASSWORD = 'SenhaForte123!'; USE Vendas; CREATE USER user_app FOR LOGIN login_app; ALTER ROLE db_datareader ADD MEMBER user_app; |
A filosofia do Postgres: role é tudo
O Postgres não tem essa separação em duas camadas. Existe um único tipo de objeto, a role, e o comando CREATE USER nada mais é do que um apelido histórico para CREATE ROLE ... WITH LOGIN. Uma role pode se conectar (se tiver o atributo LOGIN) e, ao mesmo tempo, receber permissões — é o próprio objeto de autenticação e de autorização.
|
1 2 3 4 5 6 7 |
<span style="color: #8aa9ce;">-- Estas duas linhas fazem exatamente a mesma coisa</span> CREATE USER app_user WITH PASSWORD 'SenhaForte123!'; CREATE ROLE app_user WITH LOGIN PASSWORD 'SenhaForte123!'; <span style="color: #8aa9ce;">-- Role sem LOGIN funciona como um "grupo" de permissões, sem poder autenticar</span> CREATE ROLE grupo_leitura NOLOGIN; GRANT grupo_leitura TO app_user; |
Outra consequência da fusão: uma role existe uma única vez no cluster, não por database — diferente do user do SQL Server, que é recriado (e pode ter permissões diferentes) em cada database. No Postgres, a mesma role app_user é usada para se conectar em qualquer database do cluster, e as permissões são concedidas separadamente, database a database.
E o PUBLIC do Postgres não é o mesmo PUBLIC do SQL Server. No SQL Server, public é um papel de banco de dados real, ao qual todo user pertence automaticamente. No Postgres, PUBLIC não é uma role de verdade — é uma palavra-chave que significa “toda role existente, incluindo as que ainda vão ser criadas”. GRANT SELECT ON tabela TO PUBLIC libera acesso para todo mundo, presente e futuro, e isso é fácil de conceder sem perceber a extensão do efeito.
sa x postgres: o superusuário
Os dois bancos criam um superusuário automaticamente na instalação, mas com naturezas ligeiramente diferentes. O sa é um login SQL, membro fixo do papel sysadmin; ele pode ser renomeado e, se a instância usa Windows Authentication, até desabilitado — boa prática comum de hardening. O postgres é uma role criada pelo initdb com o atributo SUPERUSER, dono do database postgres e de todos os objetos do sistema — ele também pode ser renomeado, mas na prática quase ninguém faz isso, e ele não pode ficar sem nenhum superusuário no cluster.
Uma diferença de comportamento importante: SUPERUSER no Postgres ignora todas as checagens de permissão, sem exceção — equivalente ao sysadmin. Mas, diferente do SQL Server, o Postgres não tem uma hierarquia intermediária tão rica de papéis fixos de servidor: em vez disso, oferece atributos independentes que se combinam, como CREATEDB, CREATEROLE e REPLICATION, permitindo dar poderes pontuais sem entregar superusuário completo.
|
1 2 3 4 5 |
<span style="color: #8aa9ce;">-- Poderes pontuais, sem entregar SUPERUSER completo</span> CREATE ROLE dba_operacional WITH LOGIN CREATEDB CREATEROLE PASSWORD 'SenhaForte123!'; <span style="color: #8aa9ce;">-- Ver atributos de um role</span> \du dba_operacional |
Pensando como DBA SQL Server: o hábito de logar direto como sa no dia a dia já é malvisto no mundo SQL Server — no Postgres essa recomendação é ainda mais forte. Como o postgres ignora toda checagem de permissão, um erro de digitação em um DROP ou DELETE não encontra nenhuma barreira no caminho. O padrão recomendado é reservar postgres para tarefas administrativas pontuais e criar roles com OWNER do database de aplicação para o uso do dia a dia.
Permissões default: o que já vem liberado de fábrica
No SQL Server, o papel public de cada database vem com permissões mínimas por padrão, e o user guest — que permitiria acesso sem login mapeado — vem com REVOKE CONNECT por padrão em databases novos. De modo geral, o padrão de fábrica é restritivo: quase nada funciona até que alguém conceda.
No Postgres 18, o comportamento de fábrica no schema public de um database novo já é restritivo: apenas o dono do schema (normalmente postgres) tem permissão de CREATE nele. Vale saber de onde isso veio, porque nem sempre foi assim: até a versão 14, todo database novo concedia CREATE e USAGE no schema public para PUBLIC — ou seja, qualquer role com permissão de conectar naquele database já podia criar tabelas no schema padrão, sem nenhum GRANT explícito. Esse comportamento liberal foi corrigido no PostgreSQL 15, e é a base restritiva que o Postgres 18 mantém hoje.
Cluster antigo, ainda com o comportamento velho? A mudança introduzida no PG15 vale só para databases criados a partir dessa versão (porque herdam de template1, que já vem ajustado). Um database criado em versão anterior ao 15 e depois migrado/atualizado — inclusive para o Postgres 18 atual — continua com o GRANT antigo em vigor, a menos que alguém rode o REVOKE manualmente. Vale a pena checar isso explicitamente em qualquer ambiente que já existia antes do upgrade para a versão 15.
|
1 2 3 4 5 6 7 |
<span style="color: #8aa9ce;">-- Fechar o comportamento antigo em um database criado antes do PG15</span> <span style="color: #8aa9ce;">-- (databases criados em PG15+ já nascem assim, incluindo no Postgres 18)</span> REVOKE CREATE ON SCHEMA public FROM PUBLIC; <span style="color: #8aa9ce;">-- Também vale revisar quem pode simplesmente se conectar ao database</span> REVOKE CONNECT ON DATABASE minha_base FROM PUBLIC; GRANT CONNECT ON DATABASE minha_base TO app_user, grupo_leitura; |
Passo a passo: criando role, dando acesso e ajustando permissões default
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<span style="color: #8aa9ce;">-- 1. Cria a role de aplicação, com senha e permissão de conectar</span> CREATE ROLE app_user WITH LOGIN PASSWORD 'SenhaForte123!'; GRANT CONNECT ON DATABASE minha_base TO app_user; <span style="color: #8aa9ce;">-- 2. Dá acesso ao schema e às tabelas que já existem hoje</span> GRANT USAGE ON SCHEMA vendas TO app_user; GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA vendas TO app_user; <span style="color: #8aa9ce;">-- 3. Garante que TABELAS FUTURAS criadas nesse schema já saiam com o mesmo acesso</span> <span style="color: #8aa9ce;">-- (equivalente funcional a não existir no SQL Server: lá, cada objeto novo</span> <span style="color: #8aa9ce;">-- herda do db_datareader/db_datawriter automaticamente por causa da role fixa)</span> ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA vendas GRANT SELECT, INSERT, UPDATE ON TABLES TO app_user; |
O passo 3 costuma ser o mais estranho para quem vem do SQL Server. Lá, colocar um user em db_datareader já resolve leitura para objetos futuros, porque é um papel de database inteiro. No Postgres, um GRANT comum vale só para os objetos que já existem no momento em que ele é executado — uma tabela criada depois não herda nada automaticamente, a menos que exista uma regra de ALTER DEFAULT PRIVILEGES configurada de antemão, associada a quem vai criar os objetos novos.
Comparativo lado a lado
| Conceito | SQL Server | PostgreSQL |
| Autenticação | Login (nível instância) | Role com atributo LOGIN |
| Autorização | User (nível database, mapeado ao login) | A própria role, sem objeto separado |
| Escopo do objeto de acesso | Login: instância / User: por database | Role: cluster inteiro, uma vez só |
| Superusuário padrão | sa (membro fixo de sysadmin) |
postgres (atributo SUPERUSER) |
| Papéis prontos de poder parcial | Fixed server/database roles (dbcreator, db_owner…) |
Atributos combináveis (CREATEDB, CREATEROLE…) |
| Grupo de permissões | Database role customizada + ALTER ROLE ... ADD MEMBER |
Role NOLOGIN + GRANT role TO role |
| Permissão default em schema novo | Restritiva; guest com REVOKE CONNECT por padrão |
Restritiva a partir do PG15 (era liberal antes) |
| Herança automática em objetos futuros | Sim, via papel de database (db_datareader etc.) | Não, exige ALTER DEFAULT PRIVILEGES explícito |
Erros comuns de quem vem do SQL Server
- Achar que dar GRANT hoje protege as tabelas de amanhã. Sem uma regra de
ALTER DEFAULT PRIVILEGES, cada tabela nova criada num schema nasce sem as permissões que você configurou para as tabelas antigas. - Rodar tudo como postgres, por hábito de logar como sa. Sem checagem de permissão nenhuma no meio do caminho, um comando digitado errado alcança qualquer objeto do cluster.
- Assumir que PUBLIC do Postgres é um grupo controlável como o public do SQL Server. Qualquer
GRANT ... TO PUBLICvale também para roles que ainda nem existem, o que é fácil de esquecer numa auditoria de segurança. - Herdar um cluster antigo, mesmo rodando Postgres 18, achando que já está com o padrão seguro. Databases criados antes do PG15 mantêm o
GRANT CREATE ON SCHEMA public TO PUBLIClegado até alguém revogar manualmente, mesmo depois de o cluster já ter sido atualizado para uma versão mais recente.
Resumo
No SQL Server, login e user são objetos distintos, ligados por mapeamento, e o superusuário sa é um login com o papel sysadmin. No Postgres, os dois conceitos se fundem na role, um objeto único que existe no cluster inteiro e que autentica e autoriza ao mesmo tempo; o superusuário postgres é apenas uma role com o atributo SUPERUSER. Permissões default também mudam de filosofia: o SQL Server já nasce restritivo, enquanto o Postgres 18, como todo database criado a partir da versão 15, também nasce restritivo no schema public — vale conferir esse ponto em qualquer cluster mais antigo que já existia antes do upgrade, e lembrar que objetos futuros nunca herdam permissão sozinhos sem um ALTER DEFAULT PRIVILEGES configurado de antemão.
Tem PostgreSQL em produção na sua empresa?
Faça o Power PostgreSQL Check-up: uma avaliação gratuita de performance, backup, segurança e alta disponibilidade, feita pelos nossos especialistas. Sem custo e sem compromisso.

