Defesa em Profundidade (Security Funnel)
Camada 1: VIEWS
Ocultamento de ColunasTabelas virtuais baseadas em seleções. Escondem colunas ultrassensíveis (como hashes de senhas, salários e dados fiscais) antes de expor os dados aos usuários ou sistemas.
Camada 2: DCL
GRANT & REVOKELinguagem de Controle de Dados. Define quem pode acessar quais objetos físicos (tabelas e views) e proíbe ações destrutivas como `DELETE` ou `DROP` por perfis não autorizados.
Camada 3: RLS
Row Level SecuritySegurança em Nível de Linha nativa. O SGBD intercepta todas as consultas e injeta dinamicamente regras baseadas no `auth.uid()`, garantindo que um cliente só enxergue suas próprias linhas.
Exemplo Prático Guiado (Para Apresentar aos Alunos)
Cenário do Problema: Sistema Corporativo TechCorp
A empresa contratou um estagiário de RH. Ele precisa acessar a tabela de funcionários para ver os e-mails e cargos, mas nessa *mesma* tabela estão os salários confidenciais dos Diretores. Além disso, no aplicativo de autoatendimento, um funcionário não pode ver o holerite ou pedidos de outro colega.
Criação das Tabelas de Teste com Dados Mistos
Executamos a criação da tabela de dados brutos. Observe que dados abertos (`nome`, `email`) residem na mesma tabela que dados sigilosos (`salario`, `cpf`).
-- 1. Tabela Principal de Funcionários (Dados Confidenciais)
CREATE TABLE funcionarios (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nome VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL,
cargo VARCHAR(50) NOT NULL,
cpf VARCHAR(14) NOT NULL,
salario DECIMAL(10, 2) NOT NULL
);
-- Inserindo dados de exemplo
INSERT INTO funcionarios (nome, email, cargo, cpf, salario) VALUES
('Carlos Silva', 'carlos@techcorp.com', 'Dev Senior', '123.456.789-00', 12500.00),
('Ana Souza', 'ana@techcorp.com', 'Estagiário RH', '234.567.890-11', 2200.00),
('Roberto Lima', 'roberto@techcorp.com', 'Diretor CTO', '345.678.901-22', 35000.00);
Risco do "Banco Livro Aberto"
Se dermos acesso direto a essa tabela para a API do Front-End ou para o usuário estagiário, um simples SELECT * FROM funcionarios exporá a coluna salario.
Criando a Tabela Virtual (`CREATE VIEW`)
Criamos uma visão chamada vw_funcionarios_rh. Ela projeta somente as colunas não-sensíveis.
-- 2. Criando a Visão para o Setor de RH (Ocultando CPF e Salário)
CREATE VIEW vw_funcionarios_rh AS
SELECT
id,
nome,
email,
cargo
FROM funcionarios;
-- Consultando a VIEW como se fosse uma tabela física:
SELECT * FROM vw_funcionarios_rh;
-- Para remover a visão caso necessário:
-- DROP VIEW vw_funcionarios_rh;
O Espelho Seguro
A VIEW funciona como um espelho. Ela não ocupa espaço extra em disco salvando dados duplicados. O usuário consulta a VIEW sem saber que as colunas cpf e salario existem.
Controle de Privilégios com DCL (`GRANT` e `REVOKE`)
Não adianta criar a VIEW se a role/usuário continuar com permissão de acessar a tabela original. Usamos DCL para confiscar o acesso à tabela bruta e conceder somente a leitura na VIEW.
-- 3. Criando uma Role (Perfil) para o Estagiário
CREATE ROLE perfil_estagiario_rh WITH LOGIN PASSWORD 'SenhaSegura123';
-- Confiscando qualquer acesso direto à tabela confidencial original
REVOKE ALL ON TABLE funcionarios FROM perfil_estagiario_rh;
-- Concedendo permissão APENAS de consulta (SELECT) na VIEW criada
GRANT SELECT ON vw_funcionarios_rh TO perfil_estagiario_rh;
Principio do Menor Privilégio
- • GRANT: Concede direitos (SELECT, INSERT, UPDATE, DELETE).
- • REVOKE: Confisca permissões previamente dadas.
Blindagem de Linha Nátiva com RLS e Supabase Policies
E quando o usuário precisa ver a tabela completa de pedidos, porém apenas as linhas que pertencem a ele? Ativamos o RLS e criamos uma política validando a ID do usuário logado.
-- 4. Criando tabela de Pedidos associada aos Usuários
CREATE TABLE pedidos (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
id_usuario UUID NOT NULL,
descricao VARCHAR(200) NOT NULL,
valor DECIMAL(10, 2) NOT NULL
);
-- PASSO CRÍTICO: Ativando a Segurança em Nível de Linha (RLS)
ALTER TABLE pedidos ENABLE ROW LEVEL SECURITY;
-- Criando a Política (Policy) no PostgreSQL / Supabase:
CREATE POLICY "Usuarios enxergam apenas seus proprios pedidos"
ON pedidos
FOR SELECT
USING (auth.uid() = id_usuario);
Reescrita Silenciosa de Queries
Quando a API envia:
SELECT * FROM pedidos;
O PostgreSQL com RLS reescreve internamente para:
WHERE id_usuario = auth.uid();
Simulador de Visão de Dados (Veja o Efeito da Segurança)
| ID | NOME | CARGO | CPF | SALÁRIO |
|---|