github

SECURITY DEFINER Functions in Exposed Schemas in Supabase

Supabase security checks · HIGH

This function runs with elevated rights and can be called through your API.

What goes wrong

A SECURITY DEFINER function runs with its creator’s privileges, often a superuser, and bypasses row level security (RLS) completely. In the public schema of a Supabase project, anyone can call it through /rest/v1/rpc/<function_name>.

How it happens

You need a function that reads across users, such as an admin operation or an aggregation, and mark it SECURITY DEFINER without realizing the API exposes it.

How to find it

List the functions with prosecdef = true outside the system schemas.

SELECT n.nspname AS schema, p.proname AS function_name
FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE p.prosecdef = true
  AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'extensions', 'auth', 'storage', 'pgsodium', 'vault', 'supabase_functions', 'graphql', 'graphql_public', 'realtime', '_realtime', 'pgsodium_masks', 'pgbouncer', 'net', '_analytics');

How to fix it

Move the function to a private schema that the API doesn’t expose, or revoke EXECUTE from PUBLIC, anon and authenticated. Always set a fixed search_path on it too.

-- Option 1: move it to a private schema
ALTER FUNCTION public.<function_name> SET SCHEMA private;

-- Option 2: keep it in public, fix search_path and revoke
ALTER FUNCTION public.<function_name> SET search_path = public;
REVOKE EXECUTE ON FUNCTION public.<function_name> FROM PUBLIC, anon, authenticated;

Check your own project

Locksoup runs these checks on your Supabase database in about a minute, with a read-only role, and gives you the SQL to fix what it finds.

Check my project