Usuários e Permissões

Num banco grande, muita gente usa o mesmo banco: o caixa só precisa ler os preços, o gerente pode mudar os salários, o estagiário nem deveria ver a folha de pagamento. Para isso, os bancos com servidor (MySQL, PostgreSQL, SQL Server) têm usuários e permissões: cada pessoa entra com um login e só pode fazer o que foi liberado para ela. O SQLite, que é um banco embutido num arquivo, não tem nada disso — e nesta aula você vai entender por quê, ver os comandos GRANT e REVOKE dos bancos com servidor e descobrir o que o SQLite oferece no lugar.

Você vai aprender
  • Por que o SQLite não tem usuários nem senhas (a resposta está no "embutido num arquivo").
  • Como os bancos com servidor criam usuários (CREATE USER) e dão poderes (GRANT).
  • Como tirar um poder de volta (REVOKE).
  • O princípio do menor privilégio, e por que ele protege até contra o SQL Injection.
  • O que faz as vezes de permissão no SQLite: a permissão do arquivo (no sistema), a conexão só-leitura e a view que mostra só algumas colunas.

Os comandos CREATE USER, GRANT e REVOKE são ilustrativos: eles não existem no SQLite, então não rodam no editor. Vêm da documentação oficial de cada banco. Os exemplos do que o SQLite faz no lugar rodam de verdade, no editor.


A ideia: a loja com chaves diferentes

Pense na Loja Aurora à noite, fechada. Nem todo funcionário tem a mesma chave:

  • o caixa tem a chave da frente, mas não a do cofre;
  • a gerente tem a chave do cofre e do escritório;
  • o estoquista só entra no depósito.

Cada chave abre só o que aquela pessoa precisa. Se o chaveiro do caixa for roubado, o ladrão não chega ao cofre. Num banco com servidor, as "chaves" são as permissões, e cada usuário recebe só as suas. Dar a cada um só o necessário é o princípio do menor privilégio — a regra de ouro desta aula.


1. Por que o SQLite não tem usuários

O SQLite é um banco embutido: o banco inteiro é um arquivo (o loja.db) e o "servidor" é a própria biblioteca que roda dentro do seu programa. Não há um servidor ligado o tempo todo, esperando gente se conectar com login e senha. Quem pode abrir o arquivo pode fazer tudo nele; quem não pode abrir, não faz nada.

Por isso, no SQLite, quem manda é o sistema operacional, não o banco. A permissão de ler e escrever é a permissão do arquivo loja.db (dona Ana, no computador dela). No Linux, você a vê e a muda com as ferramentas do sistema:

$ ls -l loja.db
-rw-r--r-- 1 ana ana 45056 out 18 11:14 loja.db      « a Ana lê e escreve; os outros só leem

Como o SQLite nasceu para ficar dentro de um programa (um aplicativo de celular, um navegador, um joguinho), um sistema de usuários dentro dele não faria sentido: o controle de quem usa o aplicativo é do próprio aplicativo e do sistema.

Banco embutido × banco com servidor

A aula Os Bancos Famosos explica a diferença. Em resumo: o SQLite é um arquivo que o seu programa abre (sem servidor, sem login). O MySQL, o PostgreSQL e o SQL Server são servidores: ficam ligados numa máquina, e os programas se conectam a eles por rede, cada um com um usuário e uma senha. Usuários e permissões só existem nesse segundo grupo.


2. Criar um usuário (bancos com servidor)

Nos bancos com servidor, primeiro você cria o usuário e depois dá as permissões. No MySQL, segundo a documentação oficial, é assim:

CREATE USER 'caixa'@'localhost' IDENTIFIED BY 'uma_senha_forte';
CREATE USER
"Criar usuário". Cria a conta, ainda sem nenhum poder sobre os dados.
'caixa'@'localhost'
No MySQL, o usuário é o nome mais de onde ele se conecta (localhost = a própria máquina).
IDENTIFIED BY '…'
Define a senha. Sem permissões, esse usuário entra mas não vê nada ainda.

No PostgreSQL o comando equivalente é CREATE ROLE caixa LOGIN PASSWORD '…'; e no SQL Server costuma-se criar um login no servidor e depois um usuário no banco (CREATE LOGIN … e CREATE USER …). Os nomes mudam; a ideia é a mesma: a conta nasce vazia.


3. Dar poderes: GRANT

GRANT ("conceder") é o comando que entrega uma permissão a um usuário. A forma geral é sempre a mesma: que poder, sobre o quê, para quem.

GRANT  que-poderes   ON  qual-tabela   TO  qual-usuário;

Para o nosso caixa, que só precisa ler produtos e clientes, damos só o SELECT. Veja a forma em cada banco, como aparece na documentação oficial:

GRANT SELECT, INSERT ON aurora.produtos TO 'caixa'@'localhost';
GRANT SELECT ON produtos TO caixa;
GRANT SELECT ON OBJECT::dbo.produtos TO caixa;

Depois disso, o caixa consegue SELECT na tabela produtos, mas um DELETE dele é recusado pelo banco, porque esse poder não foi concedido. Os poderes mais comuns são os próprios comandos que você já conhece: SELECT, INSERT, UPDATE, DELETE.

Só algumas colunas

Dá para ser ainda mais fino e liberar só certas colunas. No PostgreSQL, a documentação mostra o GRANT por coluna assim:

GRANT SELECT (nome, cargo) ON funcionarios TO caixa;

Agora o caixa vê nome e cargo dos funcionários, mas não o salario. Guarde essa ideia: no fim da aula você vai fazer algo parecido no SQLite, com uma view.


4. Tirar poderes de volta: REVOKE

REVOKE ("revogar") é o contrário do GRANT: tira uma permissão. A forma é igual, só trocando o TO ("para") por FROM ("de"):

REVOKE INSERT ON aurora.produtos FROM 'caixa'@'localhost';
REVOKE SELECT ON produtos FROM caixa;

Depois do REVOKE INSERT, o caixa continua lendo (ainda tem o SELECT), mas não insere mais. É assim que se corrige um poder dado a mais.

ComandoO que fazPalavra-chave de ligação
CREATE USERCria a conta (sem poderes)—
GRANTDá uma permissãoTO (para)
REVOKETira uma permissãoFROM (de)

5. O princípio do menor privilégio

A regra é simples: dê a cada usuário só o que ele precisa, e nada mais. O site que só mostra o catálogo deve conectar com um usuário que só tem SELECT. Assim, mesmo que esse site tenha uma falha de SQL Injection, o estrago é limitado: o invasor não consegue um DELETE ou um DROP TABLE, porque o usuário daquele site nunca teve esse poder.

Permissão não substitui parâmetro

O menor privilégio é uma segunda linha de defesa, não a primeira. A primeira continua sendo usar parâmetros para que o dado do visitante nunca vire SQL. As duas juntas: parâmetros para o ataque não acontecer, e poucas permissões para, se algo escapar, o dano ser pequeno.


6. O que o SQLite faz no lugar

Sem usuários, o SQLite resolve "quem pode o quê" por outros caminhos. Dois deles você roda aqui no editor.

Conexão só-leitura

Você pode abrir o banco em modo só-leitura, e aí nenhuma escrita passa. No editor, o PRAGMA query_only = ON liga esse modo. Um SELECT continua funcionando:

PRAGMA query_only = ON;
SELECT nome, salario FROM funcionarios WHERE id = 1;

Mas uma tentativa de mudar os dados é barrada, com a mensagem real do SQLite:

PRAGMA query_only = ON;
UPDATE funcionarios SET salario = 99999 WHERE id = 1;

A mensagem attempt to write a readonly database ("tentativa de escrever num banco só-leitura") é o SQLite dizendo "aqui você só lê". Um programa que só mostra dados deve abrir o banco assim — é o SELECT-only do mundo SQLite.

Uma view para mostrar só algumas colunas

Lembra do GRANT SELECT (nome, cargo), que escondia o salário? No SQLite, o mais perto disso é uma view (aula Views): uma "tabela de mentira" que mostra só as colunas que você escolher. Você cria a view com as colunas públicas e deixa o programa usar a view, não a tabela:

CREATE VIEW equipe AS
SELECT nome, cargo FROM funcionarios;

SELECT * FROM equipe;

A view equipe mostra nome e cargo dos oito funcionários, e nada do salário. Quem usa a view não tem como pedir a coluna escondida: ela não existe ali. Veja o que acontece ao tentar:

CREATE VIEW equipe AS
SELECT nome, cargo FROM funcionarios;

SELECT salario FROM equipe;

O erro no such column: salario ("não existe a coluna salario") confirma: pela view, o salário está fora de alcance. É um controle por dentro do banco, que funciona mesmo no SQLite. (Não é uma tranca de verdade — quem tem o arquivo ainda alcança a tabela original —, mas organiza bem o acesso dentro do programa.)

Nos bancos com servidor, os dois se juntam

Lá, o costume é criar a view com as colunas públicas e dar GRANT SELECT só na view, revogando o acesso à tabela crua. Aí a view deixa de ser só organização e vira uma tranca de verdade. No SQLite, falta a parte do GRANT; sobra a view como boa prática de organização.


Quando dá errado

Tentar GRANT no SQLite

Como o GRANT não existe no SQLite, escrevê-lo no editor dá erro de sintaxe — o motor não conhece a palavra:

GRANT SELECT ON produtos TO caixa;

A mensagem near "GRANT": syntax error ("erro de sintaxe perto de GRANT") é o SQLite dizendo que não entende o comando. Isso é esperado: esses comandos são só dos bancos com servidor.


Referência rápida

OndeControle de acesso
MySQL / PostgreSQL / SQL ServerCREATE USER, GRANT, REVOKE (usuários e permissões por tabela e por coluna)
SQLite — no sistemaPermissão do arquivo loja.db (quem pode abrir)
SQLite — na conexãoAbrir só-leitura (PRAGMA query_only = ON no editor)
SQLite — no bancoVIEW mostrando só algumas colunas

Exercícios

1. Por que o SQLite não tem usuários nem senhas?

2. Qual comando dá a um usuário o poder de ler uma tabela, num banco com servidor?

3. O site só mostra o catálogo. Pelo princípio do menor privilégio, com que usuário ele deve conectar?

4. Tirar um poder

Complete o comando que tira do usuário caixa o poder de inserir na tabela produtos (MySQL). Lembre a palavra que liga o comando ao usuário:

REVOKE INSERT ON aurora.produtos 'caixa'@'localhost';

Experimente: a view que esconde o salário

Missão: crie uma view chamada equipe que mostre só nome e cargo dos funcionários (sem o salário) e, depois, liste a view inteira. Devem vir 8 linhas, com as colunas nome e cargo.

-- 1. crie a view equipe só com nome e cargo
-- 2. SELECT * nela

SELECT * FROM funcionarios;
CREATE VIEW equipe AS
SELECT nome, cargo FROM funcionarios;

SELECT * FROM equipe;

Missão final: trancar a escrita

Missão: ligue o modo só-leitura e tente, de propósito, dar um aumento ao funcionário de id 1. A caixa espera o erro de banco só-leitura. (Dica: PRAGMA query_only = ON; antes do UPDATE.)

UPDATE funcionarios SET salario = 99999 WHERE id = 1;
PRAGMA query_only = ON;
UPDATE funcionarios SET salario = 99999 WHERE id = 1;

Resumo

  • Bancos com servidor (MySQL, PostgreSQL, SQL Server) têm usuários e permissões: CREATE USER cria a conta, GRANT dá poderes (com TO), REVOKE tira (com FROM), até por coluna.
  • O SQLite é um banco embutido num arquivo: não tem usuários. Quem pode abrir o arquivo faz tudo; o controle fica com o sistema operacional.
  • No lugar das permissões, o SQLite oferece: a permissão do arquivo, a conexão só-leitura (PRAGMA query_only = ON dá attempt to write a readonly database numa escrita) e a view que mostra só algumas colunas.
  • Em qualquer banco, vale o menor privilégio: dê só o necessário. É a defesa que limita o estrago se um ataque passar — mas a defesa principal continua sendo o parâmetro.