Data masking hides sensitive column values or entire rows from unauthorized users by means of views, row-level security, and conditional expressions.
Masking limits exposure of PII, PCI, or financial fields while letting analysts query the same tables. It supports GDPR, HIPAA, and SOC2 requirements without duplicating data.
Use security-barrier views for column masking, Row-Level Security (RLS) for row filtering, or a combination when both column and row restrictions apply.
Create a SECURITY_BARRIER view that shows real values to admins and obfuscated values to others, then revoke direct access to the base table.
1. CREATE ROLE read_only_role; 2. REVOKE ALL ON customers FROM PUBLIC; 3. CREATE VIEW customers_masked WITH (security_barrier) AS SELECT id, CASE WHEN current_user = 'admin' THEN email ELSE regexp_replace(email,'[^@]+','***') END AS email, created_at FROM customers; 4. GRANT SELECT ON customers_masked TO read_only_role;
Enable RLS, then write a SELECT policy that returns rows only when they belong to the current user or role.
1. ALTER TABLE orders ENABLE ROW LEVEL SECURITY; 2. CREATE POLICY orders_own ON orders FOR SELECT USING (customer_id = current_setting('app.current_customer')::int); 3. SET app.current_customer = 42 before queries.
Grant privileges on the masked view, not the base table; always add SECURITY_BARRIER; test with EXPLAIN to verify predicate pushdown; audit roles regularly.
Yes. Store raw values encrypted with pgcrypto and expose a masked or decrypted view depending on user role, giving double protection.
Yes for privileged roles; a view can reveal full data to admins while showing masked data to others. Completely irreversible masking requires overwriting the column.
Regex and CASE expressions add minor overhead, but indexes on unmasked columns remain usable. Benchmark critical queries to confirm.