Mockly

glossary

Default Privilege Drift

Default privilege drift happens when inherited grants become permissive, exposing every new object you create. This page explains it in plain English, then goes deeper into how it works in Supabase/Postgres, what commonly goes wrong, and how to fix it without relying on fragile client-side rules.

What “Default Privilege Drift” means (plain English)

Default privileges set what grants new tables or functions start with, so they must stay restrictive.

How Default Privilege Drift works in Supabase/Postgres (technical)

If default privileges still grant PUBLIC/anon/authenticated broad access, every migration or deployment spawns a new attack surface with no extra effort.

Attack paths & failure modes for Default Privilege Drift

  • Default Privilege Drift: direct API bypass: A frontend flow appears locked down, but a new table created under permissive defaults still allows client credentials through.
  • Default Privilege Drift: migration drift regression: Schema changes introduced a new table under the old permissive defaults, and no one noticed because existing tables were already hardened.
  • Default Privilege Drift: direct API bypass: The defaults granted broad access, so attackers bypassed the UI and the database returned sensitive rows.
  • Default Privilege Drift: migration drift regression: The migration inherited broad grants, so the new table became publicly accessible via client credentials.
  • The configuration doesn’t match what the UI implies (direct API access bypasses the app).
  • Policies/grants drift over time and widen access without anyone noticing.
  • Fixes are applied without verification, leading to false confidence.

Why Default Privilege Drift matters for Supabase security

You can harden existing tables but still leak data the moment a migration runs with permissive defaults. If Default Privilege Drift remains unresolved, attackers can automate enumeration and unauthorized writes at API speed. Treat it as a production reliability risk as well as a data security risk, because incidents spread quickly once clients discover weak access boundaries.

Common Default Privilege Drift mistakes that lead to leaks

  • Not re-checking default privileges after role or environment changes.
  • Assuming defaults stay secure because existing tables were locked down.
  • Skipping ALTER DEFAULT PRIVILEGES when new roles are introduced.
  • Default Privilege Drift: direct API bypass: The defaults granted broad access, so attackers bypassed the UI and the database returned sensitive rows.
  • Default Privilege Drift: migration drift regression: The migration inherited broad grants, so the new table became publicly accessible via client credentials.

Where to look for Default Privilege Drift in Supabase

  • Your grants, policies, and any direct client access paths.
  • Storage and RPC settings (common blind spots).

How to detect Default Privilege Drift issues (signals + checks)

Use this as a quick checklist to validate your current state:

  • Try the same queries your frontend can run (anon/authenticated). If sensitive rows come back, you have exposure.
  • Verify RLS is enabled and (for sensitive tables) forced.
  • List policies and look for conditions that don’t bind rows to a user or tenant.
  • Audit grants to anon / authenticated on sensitive tables and functions.
  • Default Privilege Drift: direct API bypass: Default privilege drift is easy to miss.
  • Default Privilege Drift: direct API bypass: Set defaults to deny-by-default and audit them regularly.
  • Default Privilege Drift: direct API bypass: Always check grant changes in migrations.
  • Re-test after every migration that touches security-critical tables or functions.

How to fix Default Privilege Drift (backend-only + zero-policy posture)

Mockly’s safest default is backend-only access: the browser should not query tables, call RPC, or access Storage directly.

  1. Decide which operations must remain client-side (often: none for sensitive resources).
  2. Create server endpoints (API routes or server actions) for required reads/writes.
  3. Apply hardening SQL: enable+force RLS where relevant, remove broad policies, and revoke grants from client roles.
  4. Generate signed URLs for private Storage downloads on the server only.
  5. Re-run a scan and confirm the issue disappears.
  6. Add a regression check to your release process so drift doesn’t reintroduce exposure. Fixes that worked in linked incidents:
  • Default Privilege Drift: direct API bypass: Lock down default privileges, enforce backend-only endpoints, and audit new grants as part of each release.
  • Default Privilege Drift: migration drift regression: Add migration-time guards that verify default privileges, tighten inherited grants, and document the intended security posture.

Verification checklist for Default Privilege Drift

  1. Attempt direct access using client credentials and confirm it fails.
  2. Apply a backend-only fix pattern and verify end-to-end behavior.
  3. Re-run a scan after changes and after the next migration.
  4. Default Privilege Drift: direct API bypass: Default privilege drift is easy to miss.
  5. Default Privilege Drift: direct API bypass: Set defaults to deny-by-default and audit them regularly.
  6. Default Privilege Drift: direct API bypass: Always check grant changes in migrations.
  7. Default Privilege Drift: direct API bypass: Test direct API calls after deploying schema changes.
  8. Default Privilege Drift: migration drift regression: Migrations can reset default privileges inadvertently.

SQL sanity checks for Default Privilege Drift (optional, but high signal)

If you prefer evidence over intuition, run a small set of SQL checks after each fix.

The goal is not to memorize catalog tables — it’s to make sure the access boundary you intended is the one Postgres actually enforces:

  • Confirm RLS is enabled (and forced where appropriate) for tables tied to this term.
  • List policies and read them as plain language: who can do what, under what condition?
  • Audit grants for anon/authenticated and PUBLIC on the tables, views, and functions involved.
  • If Storage is involved: review bucket privacy and policies for listing/reads.
  • If RPC is involved: review EXECUTE grants for functions and whether privileged functions are server-only.

Pair these checks with a direct API access test using client credentials. When both agree, you can ship the fix with confidence.

Over time, keep a small “query pack” for the checks you trust and run it after every migration. That’s how you prevent quiet regressions.

Prevent Default Privilege Drift drift (so it doesn’t come back)

  • Add a repeatable checklist and re-run it after schema changes.
  • Prefer backend-only access for sensitive resources.
  • Keep one reusable verification test for “Default Privilege Drift: direct API bypass” and rerun it after every migration that touches this surface.
  • Keep one reusable verification test for “Default Privilege Drift: migration drift regression” and rerun it after every migration that touches this surface.

Rollout plan for Default Privilege Drift fixes (without breaking production)

Many hardening changes fail because teams revoke direct access first and only later discover missing backend paths.

Use this sequence to reduce both risk and outage pressure:

  1. Implement and verify the backend endpoint or server action before permission changes.
  2. Switch clients to that backend path behind a feature flag when possible.
  3. Then revoke direct client access (broad grants, permissive policies, public bucket reads, or broad EXECUTE).
  4. Run direct-access denial tests and confirm authorized backend flows still succeed.
  5. Re-scan after deployment and again after the next migration.

This turns security fixes into repeatable rollout mechanics instead of one-off emergency changes.

Incident breakdowns for Default Privilege Drift (real scenarios)

Default Privilege Drift: direct API bypass

Scenario: A frontend flow appears locked down, but a new table created under permissive defaults still allows client credentials through.

What failed: The defaults granted broad access, so attackers bypassed the UI and the database returned sensitive rows.

What fixed it: Lock down default privileges, enforce backend-only endpoints, and audit new grants as part of each release.

Why the fix worked: New objects now inherit secure defaults, so the direct API path dies before it reaches production.

Key takeaways:

  • Default privilege drift is easy to miss.
  • Set defaults to deny-by-default and audit them regularly.
  • Always check grant changes in migrations.
  • Test direct API calls after deploying schema changes.

Read full example: Default Privilege Drift: direct API bypass

Default Privilege Drift: migration drift regression

Scenario: Schema changes introduced a new table under the old permissive defaults, and no one noticed because existing tables were already hardened.

What failed: The migration inherited broad grants, so the new table became publicly accessible via client credentials.

What fixed it: Add migration-time guards that verify default privileges, tighten inherited grants, and document the intended security posture.

Why the fix worked: Default privileges now stay locked down, so future migrations cannot slip a new public surface into the app.

Key takeaways:

  • Migrations can reset default privileges inadvertently.
  • CI should validate inherited grants on deploy.
  • Document the intended security posture for every new object.
  • Track security state before and after schema changes.

Read full example: Default Privilege Drift: migration drift regression

Real-world examples of Default Privilege Drift (and why they work)

Related terms

  • Broad DELETE for Authenticated Role → /glossary/broad-authenticated-delete
  • Schema USAGE Granted to PUBLIC → /glossary/schema-usage-granted-to-public

FAQ

Is Default Privilege Drift enough to secure my Supabase app?

It’s necessary, but not sufficient. You also need correct grants, secure Storage/RPC settings, and a backend-only access model for sensitive operations.

What’s the quickest way to reduce risk with Default Privilege Drift?

Remove direct client access to sensitive resources, enable/force RLS where appropriate, and verify via a repeatable checklist that anon/authenticated cannot query what they shouldn’t.

How do I verify the fix is real (not just a UI change)?

Attempt direct API queries using the same client credentials your app ships. If the database denies access (401/403) and your backend endpoints still work, your fix is effective.

Next step

Want a quick exposure report for your own project? Run a scan in Mockly to find public tables, storage buckets, and RPC functions — then apply fixes with verification steps.

Explore related pages

parent

Glossary

/glossary

sibling

Broad DELETE for Authenticated Role

/glossary/broad-authenticated-delete

sibling

Schema USAGE Granted to PUBLIC

/glossary/schema-usage-granted-to-public

cross

Lock down a public table (backend-only access)

/templates/access-control/lock-down-public-table

cross

Remove over-permissive RLS policies (adopt deny-by-default)

/templates/access-control/remove-over-permissive-policies

cross

Pricing

/pricing