How can a project owner revoke PUBLIC privileges on the net schema (pg_net)?
#49421
Replies: 4 comments
|
Regarding the
|
|
Your analysis is spot-on, and the fact that you've already measured the actual PostgREST surface ( But your instinct about the "bridge" vector is exactly right. Here's a concrete way to monitor for it: -- Check if any role with API access has direct EXECUTE on net functions
SELECT
r.rolname AS grantee,
has_function_privilege(r.rolname, 'net.http_post(text, jsonb, text)', 'EXECUTE') AS can_http_post,
has_schema_privilege(r.rolname, 'net', 'USAGE') AS has_net_usage
FROM pg_roles r
WHERE r.rolname IN ('anon', 'authenticated', 'service_role');And a query to find SECURITY DEFINER functions in exposed schemas that reference SELECT p.proname, n.nspname, pg_get_functiondef(p.oid)
FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE n.nspname IN ('public', 'graphql_public')
AND p.prosecdef = true
AND pg_get_functiondef(p.oid) ~ 'net\\.';This gives you a tripwire: if someone creates a On the self-serve revocation question: as others noted, -- Revoke broad EXECUTE, then re-grant only what's needed
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA public FROM anon;
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA public FROM authenticated;
GRANT EXECUTE ON FUNCTION your_safe_function(args) TO authenticated;This doesn't close the |
|
Agree with the analysis above: you can't revoke it because supabase_admin owns the extension, and PostgREST doesn't expose net. But the "bridge" you're worried about is worth spelling out, because I hit it in production and it's the part you can control. On a Supabase project, every function you create in public is executable by anon and authenticated out of the box: the platform sets default privileges for the postgres role that grant EXECUTE to those API roles. So the real question isn't whether net is readable, it's whether any SECURITY DEFINER function reachable via /rest/v1/rpc/... calls into it (or into anything else sensitive). In my case a definer function that read a payment-provider token was callable with the public anon key. Nothing in net, same shape of problem. What fixed it, and what I'd do here: -- close the ones that already exist Verify from the role's point of view, not as postgres: and from outside, curl -X POST .../rest/v1/rpc/trigger_job -H "apikey: " should now come back with a permission error instead of running. The tripwire query cekuu35 posted is a good complement: run it in CI so a new definer function that touches net.* fails the build. On your question 2: I haven't tested toggling the extension, but the grants come from the extension's own install script, so I'd expect them to come back identical. |

Uh oh!
There was an error while loading. Please reload this page.
On a hosted Supabase project (Postgres 17), the
pg_netobjects are granted toPUBLIC, which transitively includesanon:net.http_request_queuestores the headers of outgoing requests, so anything able to read it can read whateverAuthorizationor custom auth headers yourpg_cronjobs send to Edge Functions.The part I'm stuck on: I can't revoke it
The ACL is unchanged afterwards, because:
postgresis neither superuser nor a member of the owning role, so the REVOKE is a no-op. Worth highlighting for anyone else hitting this: it does not raise an error, only a WARNING — a migration that does this looks like it succeeded and silently changes nothing.Questions
netschema toPUBLICintentional on hosted projects, or a packaging artifact?net?What I measured on my side, in case it helps others who find this
The grant is open, but it is not reachable through the API. Using the project's anon key:
So it only becomes exploitable if something in an exposed schema bridges to it — e.g. a
publicfunction that callsnet.*and is executable byanon. I now monitor for exactly that condition, but I'd rather close the grant itself than rely on a tripwire.All reactions