glossary
Public RPC Surface Area
Public RPC surface area is every function you leave EXECUTE access to client roles, and it tends to grow into a leak. 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 “Public RPC Surface Area” means (plain English)
RPCs callable by anon/authenticated roles become API endpoints attackers can hit directly. Public RPC Surface Area is a practical security issue for teams using Supabase because exposed tables, storage, or RPC endpoints can return or mutate data beyond intended account boundaries.
How Public RPC Surface Area works in Supabase/Postgres (technical)
When functions retain PUBLIC/anon EXECUTE, an attacker can bypass frontend checks, especially if the RPC trusts caller parameters or returns joins that expose sensitive rows.
Attack paths & failure modes for Public RPC Surface Area
- Public helper function becomes a data leak: The function powers an autocomplete but product later expands it and the grant remains PUBLIC.
- RPC parameter tampering reads other users: An RPC like
get_user_report(user_id uuid)was exposed to the frontend to fetch reports. - Public helper function becomes a data leak: EXECUTE was still granted to client roles, so attackers called it directly with parameters that widen the output.
- RPC parameter tampering reads other users: Attackers altered the parameter and read data for arbitrary users even though the UI never offered that option.
- Public EXECUTE privileges let attackers call internal helper functions directly via RPC.
- Functions trust caller-supplied identifiers, enabling parameter tampering (read/write other users’ data).
- Overloaded signatures are missed during lock-down, leaving a callable variant exposed.
- RPCs evolve over time (more joins, more fields) and quietly become sensitive without a permission review.
Why Public RPC Surface Area matters for Supabase security
Attackers hit those RPCs directly to run privileged business logic or to fetch data you never intended to share. If Public RPC Surface Area 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 Public RPC Surface Area mistakes that lead to leaks
- Leaving helper RPCs public as requirements change.
- Trusting caller-supplied identifiers inside the function.
- Failing to audit RPC outputs alongside tables.
- Public helper function becomes a data leak: EXECUTE was still granted to client roles, so attackers called it directly with parameters that widen the output.
- RPC parameter tampering reads other users: Attackers altered the parameter and read data for arbitrary users even though the UI never offered that option.
Where to look for Public RPC Surface Area in Supabase
- Function inventory and EXECUTE grants for PUBLIC/anon/authenticated.
- Functions that accept identity parameters (user_id/tenant_id) or return large result sets.
- Any client-side code paths calling
rpc(...)directly.
How to detect Public RPC Surface Area 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/authenticatedon sensitive tables and functions. - Public helper function becomes a data leak: RPCs drift as requirements change.
- Public helper function becomes a data leak: Public EXECUTE turns functions into APIs.
- Public helper function becomes a data leak: Server-only execution allows logging and validation.
- Re-test after every migration that touches security-critical tables or functions.
How to fix Public RPC Surface Area (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.
- Decide which operations must remain client-side (often: none for sensitive resources).
- Create server endpoints (API routes or server actions) for required reads/writes.
- Apply hardening SQL: enable+force RLS where relevant, remove broad policies, and revoke grants from client roles.
- Generate signed URLs for private Storage downloads on the server only.
- Re-run a scan and confirm the issue disappears.
- Add a regression check to your release process so drift doesn’t reintroduce exposure. Fixes that worked in linked incidents:
- Public helper function becomes a data leak: Revoke EXECUTE, run it only from server code with service_role, and define a strict response shape.
- RPC parameter tampering reads other users: Revoke public grants and move the operation behind a backend endpoint that derives identity from the session and ignores caller-supplied identity fields.
Verification checklist for Public RPC Surface Area
- Enumerate functions and identify which are callable from client roles.
- Attempt to call sensitive functions as anon/authenticated and confirm they are denied.
- Try parameter tampering (swap identifiers) and ensure server-side identity derivation prevents abuse.
- Re-run the audit after every migration that adds/changes functions.
- Public helper function becomes a data leak: RPCs drift as requirements change.
- Public helper function becomes a data leak: Public EXECUTE turns functions into APIs.
- Public helper function becomes a data leak: Server-only execution allows logging and validation.
- Public helper function becomes a data leak: Define a strict response shape to prevent accidental leaks.
SQL sanity checks for Public RPC Surface Area (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 Public RPC Surface Area drift (so it doesn’t come back)
- Prefer server-only wrappers for privileged functions (backend endpoints + service_role).
- Lock down EXECUTE by default; explicitly grant only what you intend to expose.
- Add a function review checklist for code review and migrations.
- Keep one reusable verification test for “Public helper function becomes a data leak” and rerun it after every migration that touches this surface.
- Keep one reusable verification test for “RPC parameter tampering reads other users” and rerun it after every migration that touches this surface.
Rollout plan for Public RPC Surface Area 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:
- Implement and verify the backend endpoint or server action before permission changes.
- Switch clients to that backend path behind a feature flag when possible.
- Then revoke direct client access (broad grants, permissive policies, public bucket reads, or broad EXECUTE).
- Run direct-access denial tests and confirm authorized backend flows still succeed.
- 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 Public RPC Surface Area (real scenarios)
Public helper function becomes a data leak
Scenario: The function powers an autocomplete but product later expands it and the grant remains PUBLIC.
What failed: EXECUTE was still granted to client roles, so attackers called it directly with parameters that widen the output.
What fixed it: Revoke EXECUTE, run it only from server code with service_role, and define a strict response shape.
Why the fix worked: Backend execution enforces authorization, logs calls, and controls joins before the function runs.
Key takeaways:
- RPCs drift as requirements change.
- Public EXECUTE turns functions into APIs.
- Server-only execution allows logging and validation.
- Define a strict response shape to prevent accidental leaks.
Read full example: Public helper function becomes a data leak
RPC parameter tampering reads other users
Scenario: An RPC like get_user_report(user_id uuid) was exposed to the frontend to fetch reports.
What failed: Attackers altered the parameter and read data for arbitrary users even though the UI never offered that option.
What fixed it: Revoke public grants and move the operation behind a backend endpoint that derives identity from the session and ignores caller-supplied identity fields.
Why the fix worked: Identity and authorization decisions happen server-side, so changing the parameter no longer tricks the function.
Key takeaways:
- Don’t trust caller-supplied identity parameters.
- Treat RPCs like endpoints and authorize every call.
- Backend-only execution makes identity derivation safer.
- Test abuse by calling RPCs with modified inputs.
Read full example: RPC parameter tampering reads other users
Real-world examples of Public RPC Surface Area (and why they work)
- Public helper function becomes a data leak — A helper RPC stayed public and eventually returned sensitive rows.
- RPC parameter tampering reads other users — Functions that trust caller-supplied IDs let attackers read other users’ data.
Related terms
- RPC EXECUTE Grants →
/glossary/rpc-execute-grants - Service Role Key →
/glossary/service-role-key
FAQ
Is Public RPC Surface Area 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 Public RPC Surface Area?
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.