Nguồn: PlanetScale Blog
Tóm tắt
Row Level Security (RLS) trong PostgreSQL cho phép định nghĩa security policy trực tiếp trong database thay vì application layer — mỗi query tự động bị lọc theo policy, không cần code ứng dụng xử lý. Tuy nhiên, PlanetScale lập luận rằng trong thực tế, footgun và gotcha của RLS thường outweigh lợi ích.
Vấn đề đầu tiên là connection pooling. Postgres dùng kiến trúc process-per-connection, nên cần PgBouncer để pool connections. Khi dùng PgBouncer, upstream identity của client bị mất — không thể dùng Postgres role để define policy. Giải pháp workaround là dùng session-local variable (SET LOCAL app.tenant_id = '1234'), nhưng điều này yêu cầu wrap mọi transaction trong ứng dụng — nếu quên một chỗ, policy fail silently hoặc throw error.
Về attack surface: RLS hoạt động như một WHERE clause extra, nhưng không ngăn query chạy — nó chỉ filter kết quả. Một user độc hại vẫn có thể tạo load lớn trên database bằng cách spam queries qua API endpoint, tiêu tốn CPU của các user khác, kể cả khi không truy cập được data. RLS không thể thay thế application-level authentication và rate limiting.
Performance cũng là vấn đề: các function trong RLS policy chạy per-row, không phải per-query. Benchmark của PlanetScale cho thấy sự khác biệt đáng kể giữa VOLATILE function (default), STABLE function, và các biến thể có cache. Kết luận: quản lý access control trong application code — gần với logic business — dễ maintain và test hơn là phân tán policy vào database layer.