Skip to content

Commit

Permalink
docs for foreign table in api lint
Browse files Browse the repository at this point in the history
  • Loading branch information
olirice committed May 10, 2024
1 parent daff23e commit 02da64c
Showing 1 changed file with 33 additions and 8 deletions.
41 changes: 33 additions & 8 deletions docs/0017_foreign_table_in_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,52 @@ Level: WARN

### Rationale

Materialized views in Postgres can present a security risk if they are accessible to API roles `anon` and `authenticated`. Unlike regular views, materialized views can not be configured to respect Row Level Security (RLS) policies of the underlying tables they are built upon, nor can they cannot be secured with RLS directly. Therefore, if materialized views are accessible over APIs, all rows are always visible, which may not be intended.

### The Risk of Materialized Views Accessible by Anon or Authenticated Roles

If materialized views are exposed in APIs and accessible by the `anon` or `authenticated` roles, API users bypass any Row-Level Security (RLS) policies implemented on the underlying tables. This can lead to unintended exposure of sensitive data as all users will be able to select all rows of data from the materialized views.
Foreign Tables in Postgres can present a security risk if they are accessible to API roles `anon` and `authenticated`. Unlike regular tables, foreign tables can not be configured to respect Row Level Security (RLS) policies.. Therefore, if foreign tables are accessible over APIs, all rows are always visible, which may not be intended.

### How to Resolve

To mitigate the risk it is recommended to revoke `select` access from API roles `anon` and `authenticated`.
If the foreign table does not need to be accessible over the API you can resolve the issue by revoking `select` access from API roles `anon` and `authenticated`.

```sql
revoke select on public.some_mat_view from public, anon, authenticated;
revoke select on public.some_foreign_table from public, anon, authenticated;
```

Note that the `public` role is a role that sets default permissions for all other roles. If the `public` role allows access by default (as it does in the `public` schema) you must also revoke `select` accesss from it.

You can test if your permissions update worked sucessfully by running

```sql
select pg_catalog.has_table_privilege('anon', 'public.some_mat_view'::regclass::oid, 'select')
select pg_catalog.has_table_privilege('anon', 'public.some_foreign_table'::regclass::oid, 'select')
-- Should return: 'false'
```

Substituting in the appropriate role and view name.

If you do need to access data from the foreign table over APIs we recommend moving the foreign table out of the API's search path and then creating a function, accessible [over RPC](https://supabase.com/docs/reference/javascript/rpc), that implements security rules on top of the foreign table. For example, if we wanted to confirm that the Supabase Auth user matches the `author_id` column of the foreign table the function might look like:

```sql
-- Create a new schema
create schema private;

-- Move the foreign table out of the API search path
alter foreign table public.some_foreign_table set schema private;

-- Make sure the API roles still have access to the FDW
grant select on public.some_foreign_table to anon, authenticated;

-- Create a function/RPC target with security rules
create or replace function fdw_wrapping_function()
returns table (id integer, data text, author_id uuid)
language sql
set search_path = ''
as $$
select
id,
data,
author_id
from
private.some_foreign_table
where
author_id = (select auth.uid()); -- SECURITY RULE
$$;
```

0 comments on commit 02da64c

Please sign in to comment.