A single misconfigured policy can give an intern the same power as an admin.
Privilege escalation through Row-Level Security (RLS) is one of the most dangerous mistakes you can make in database access control. On paper, RLS looks airtight. You define filters for each user or role, the database enforces them automatically, and you trust that no query can return rows beyond what’s allowed. But the cracks appear when you combine complex permissions, role inheritance, and overlooked SQL paths. That’s where attackers slip in.
How Privilege Escalation Happens with RLS
RLS works by adding hidden WHERE clauses to queries based on the current user or role. The danger comes when your policy logic assumes too much. A subquery that runs outside the RLS context. A JOIN that leaks rows through an unfiltered side table. Functions that return sets without applying the correct policy. Role switching inside a session that bypasses the intended scope. Each of these is a real-world path to silently escalating privileges.
Poorly scoped filters let a user see more than they should. Multi-tenant apps with shared schemas are even more exposed. If the RLS predicate checks only basic attributes like tenant_id, but those attributes can be spoofed through an injectable function or a forgotten UPDATE permission, the wall falls in seconds.
The Test Most Teams Skip
Teams often test RLS from the happy path. They check that a normal query returns the right rows. But they don’t run adversarial queries that try to bypass the policy. They don’t check unsafe role grants that enable privilege chaining. They don’t audit stored procedures to confirm that RLS applies consistently. Even well-written policies can be defeated if the database session changes its identity mid-query.