Thread: PG16.1 security breach?
I am running the following on Postgres 16.1 in database "postgres" as a superuser:
revoke create on schema public from public;
revoke create on database postgres from public;
create schema if not exists oiz;
revoke create on schema oiz from public;
grant usage on schema oiz to public;
create or replace function oiz.f_set_dbowner (p_dbowner text, p_dbname text)
returns void
language plpgsql
security definer
as $$
…
when I create a new role in following:
create role testuser with password 'testuser' login;
postgres=# \du testuser
List of roles
Role name | Attributes
-----------+------------
testuser |
than this new role is able to execute the function oiz.f_set_dbowner immediately even I did not grant execute on this function to this role!
postgres=> \conninfo
You are connected to database "postgres" as user "testuser" on host "cmpgdb-pg-eng900.eng.cmp.szh.loc" (address "10.199.112.56") at port "5017".
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
postgres=> select oiz.f_set_dbowner ('testuser','database1');
f_set_dbowner
---------------
(1 row)
The role is also able to execute the function even I revoke any execute privilege explicitly:
revoke execute on function oiz.f_set_dbowner (p_dbowner text, p_dbname text) from testuser;
There are also no default privileges on the schema:
postgres=# \ddp
Default access privileges
Owner | Schema | Type | Access privileges
-------+--------+------+-------------------
(0 rows)
postgres=> \df+ oiz.f_set_dbowner
List of functions
Schema | Name | Result data type | Argument data types | Type | Volatility | Parallel | Owner | Security | Access privileges | Language | Internal name | Description
--------+---------------+------------------+-------------------------------+------+------------+----------+----------+----------+---------------------+----------+---------------+-------------
oiz | f_set_dbowner | void | p_dbowner text, p_dbname text | func | volatile | unsafe | postgres | definer | =X/postgres +| plpgsql | |
| | | | | | | | | postgres=X/postgres | | |
(1 row)
postgres=> \l postgres
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges
----------+----------+----------+-----------------+-------------+-------------+------------+-----------+-----------------------
postgres | postgres | UTF8 | libc | de_CH.utf-8 | de_CH.utf-8 | | | =Tc/postgres +
| | | | | | | | postgres=CTc/postgres
(1 row)
What I am missing? Is there something new with PG 16? Is it a bug?
Cheers, Markus
On 6/7/24 07:04, Zwettler Markus (OIZ) wrote: > I am running the following on Postgres 16.1 in database "postgres" as a > superuser: <snip> > create or replace function oiz.f_set_dbowner (p_dbowner text, p_dbname text) <snip> > create role testuser with password 'testuser' login; <snip> > than this new role is able to execute the function oiz.f_set_dbowner > immediately even I did not grant execute on this function to this role! See: https://www.postgresql.org/docs/current/sql-createfunction.html In particular, this part: 8<------------------------ Another point to keep in mind is that by default, execute privilege is granted to PUBLIC for newly created functions (see Section 5.7 for more information). Frequently you will wish to restrict use of a security definer function to only some users. To do that, you must revoke the default PUBLIC privileges and then grant execute privilege selectively. To avoid having a window where the new function is accessible to all, create it and set the privileges within a single transaction. For example: 8<------------------------ HTH, -- Joe Conway PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
> -----Ursprüngliche Nachricht----- > Von: Joe Conway <mail@joeconway.com> > Gesendet: Freitag, 7. Juni 2024 15:22 > An: Zwettler Markus (OIZ) <Markus.Zwettler@zuerich.ch>; pgsql- > general@lists.postgresql.org > Betreff: [Extern] Re: PG16.1 security breach? > > On 6/7/24 07:04, Zwettler Markus (OIZ) wrote: > > I am running the following on Postgres 16.1 in database "postgres" as > > a > > superuser: > > <snip> > > > create or replace function oiz.f_set_dbowner (p_dbowner text, p_dbname > > text) > > <snip> > > > create role testuser with password 'testuser' login; > > <snip> > > > than this new role is able to execute the function oiz.f_set_dbowner > > immediately even I did not grant execute on this function to this role! > > See: > https://www.postgresql.org/docs/current/sql-createfunction.html > > In particular, this part: > 8<------------------------ > Another point to keep in mind is that by default, execute privilege is granted to > PUBLIC for newly created functions (see Section 5.7 for more information). > Frequently you will wish to restrict use of a security definer function to only some > users. To do that, you must revoke the default PUBLIC privileges and then grant > execute privilege selectively. > To avoid having a window where the new function is accessible to all, create it and > set the privileges within a single transaction. For example: > 8<------------------------ > > HTH, > > -- > Joe Conway > PostgreSQL Contributors Team > RDS Open Source Databases > Amazon Web Services: https://aws.amazon.com > > --- Externe Email: Vorsicht mit Anhängen, Links oder dem Preisgeben von > Informationen --- Argh. No! What a bad habit! Might be good idea for an enhancement request to create a global parameter to disable this habit. Thanks Markus
grant usage on schema oiz to public;
The role is also able to execute the function even I revoke any execute privilege explicitly:
revoke execute on function oiz.f_set_dbowner (p_dbowner text, p_dbname text) from testuser;
There are also no default privileges on the schema:
On Fri, 2024-06-07 at 13:54 +0000, Zwettler Markus (OIZ) wrote: > > Another point to keep in mind is that by default, execute privilege is granted to > > PUBLIC for newly created functions (see Section 5.7 for more information). > > Argh. No! What a bad habit! > > Might be good idea for an enhancement request to create a global parameter to disable this habit. I don't see the problem, since the default execution mode for functions is SECURITY INVOKER. But you can easily change that: ALTER DEFAULT PRIVILEGES FOR ROLE function_creator REVOKE EXECUTE ON FUNCTION FROM PUBLIC; Yours, Laurenz Albe
On 6/7/24 06:54, Zwettler Markus (OIZ) wrote: >> -----Ursprüngliche Nachricht----- >> Von: Joe Conway <mail@joeconway.com> >> Gesendet: Freitag, 7. Juni 2024 15:22 >> An: Zwettler Markus (OIZ) <Markus.Zwettler@zuerich.ch>; pgsql- >> general@lists.postgresql.org >> Betreff: [Extern] Re: PG16.1 security breach? >> >> On 6/7/24 07:04, Zwettler Markus (OIZ) wrote: > > Argh. No! What a bad habit! > > Might be good idea for an enhancement request to create a global parameter to disable this habit. Read this https://www.postgresql.org/docs/current/ddl-priv.html through several times, it will make things clearer. In particular the part that starts "PostgreSQL grants privileges on some types of objects to PUBLIC by default when the objects are created. ..." > > Thanks Markus > -- Adrian Klaver adrian.klaver@aklaver.com
On Fri, 2024-06-07 at 13:54 +0000, Zwettler Markus (OIZ) wrote:
> > Another point to keep in mind is that by default, execute privilege is granted to
> > PUBLIC for newly created functions (see Section 5.7 for more information).
>
> Argh. No! What a bad habit!
>
> Might be good idea for an enhancement request to create a global parameter to disable this habit.
I don't see the problem, since the default execution mode for functions is
SECURITY INVOKER.
But you can easily change that:
ALTER DEFAULT PRIVILEGES FOR ROLE function_creator REVOKE EXECUTE ON FUNCTION FROM PUBLIC;
On Fri, 2024-06-07 at 07:42 -0700, David G. Johnston wrote: > On Friday, June 7, 2024, Laurenz Albe <laurenz.albe@cybertec.at> wrote: > > On Fri, 2024-06-07 at 13:54 +0000, Zwettler Markus (OIZ) wrote: > > > > Another point to keep in mind is that by default, execute privilege is granted to > > > > PUBLIC for newly created functions (see Section 5.7 for more information). > > > > > > Argh. No! What a bad habit! > > > > > > Might be good idea for an enhancement request to create a global parameter to disable this habit. > > > > I don't see the problem, since the default execution mode for functions is > > SECURITY INVOKER. > > > > But you can easily change that: > > > > ALTER DEFAULT PRIVILEGES FOR ROLE function_creator REVOKE EXECUTE ON FUNCTION FROM PUBLIC; > > You named function_creator here when in this example the role creating the new object is postgres. Then use "postgres" rather than "function_creator". An ALTER DEFAULT PRIVILEGES statement always only changes default privileges for objects created by a certain user. > How is it that the default privilege granted to public doesn’t seem to care who the object creator > is yet when revoking the grant one supposedly can only do so within the scope of a single role? I don't understand what you wrote. ALTER DEFAULT PRIVILEGES also only applies to objects created by a single role when you grant default privileges. Yours, Laurenz Albe
> How is it that the default privilege granted to public doesn’t seem to care who the object creator
> is yet when revoking the grant one supposedly can only do so within the scope of a single role?
I don't understand what you wrote. ALTER DEFAULT PRIVILEGES also only applies to objects
created by a single role when you grant default privileges.
On Mon, Jun 10, 2024 at 2:21 AM Laurenz Albe <laurenz.albe@cybertec.at> wrote:> How is it that the default privilege granted to public doesn’t seem to care who the object creator
> is yet when revoking the grant one supposedly can only do so within the scope of a single role?
I don't understand what you wrote. ALTER DEFAULT PRIVILEGES also only applies to objects
created by a single role when you grant default privileges.I think my point is that a paragraph like the following may be a useful addition:If one wishes to remove the default privilege granted to public to execute all newly created procedures it is necessary to revoke that privilege for every superuser in the system
On Wed, Jun 12, 2024 at 4:36 PM David G. Johnston <david.g.johnston@gmail.com> wrote:On Mon, Jun 10, 2024 at 2:21 AM Laurenz Albe <laurenz.albe@cybertec.at> wrote:> How is it that the default privilege granted to public doesn’t seem to care who the object creator
> is yet when revoking the grant one supposedly can only do so within the scope of a single role?
I don't understand what you wrote. ALTER DEFAULT PRIVILEGES also only applies to objects
created by a single role when you grant default privileges.I think my point is that a paragraph like the following may be a useful addition:If one wishes to remove the default privilege granted to public to execute all newly created procedures it is necessary to revoke that privilege for every superuser in the systemThat seems... excessive. You can revoke other privs from public (can't you?), so why seemingly only do procedures/functions have this difficulty.
Ron Johnson <ronljohnsonjr@gmail.com> writes: > On Wed, Jun 12, 2024 at 4:36 PM David G. Johnston < > david.g.johnston@gmail.com> wrote: >> I think my point is that a paragraph like the following may be a useful >> addition: >> >> If one wishes to remove the default privilege granted to public to execute >> all newly created procedures it is necessary to revoke that privilege for >> every superuser in the system > That seems... excessive. More to the point, it's wrong. Superusers have every privilege there is "ex officio"; we don't even bother to look at the catalog entries when considering a privilege check for a superuser. Revoking their privileges will accomplish nothing, and it does nothing about the actual source of the problem (the default grant to PUBLIC) either. What I'd do if I didn't like this policy is some variant of ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC; Repeat for each schema that you think might be publicly readable (which is only public by default). BTW, in PG 15 and up, the public schema is not writable by default, which attacks basically the same problem from a different direction. regards, tom lane
Ron Johnson <ronljohnsonjr@gmail.com> writes:
> On Wed, Jun 12, 2024 at 4:36 PM David G. Johnston <
> david.g.johnston@gmail.com> wrote:
>> I think my point is that a paragraph like the following may be a useful
>> addition:
>>
>> If one wishes to remove the default privilege granted to public to execute
>> all newly created procedures it is necessary to revoke that privilege for
>> every superuser in the system
> That seems... excessive.
More to the point, it's wrong. Superusers have every privilege there
is "ex officio"; we don't even bother to look at the catalog entries
when considering a privilege check for a superuser. Revoking their
privileges will accomplish nothing, and it does nothing about the
actual source of the problem (the default grant to PUBLIC) either.
every superuser in the system.
On 6/12/24 18:56, Tom Lane wrote: > Ron Johnson <ronljohnsonjr@gmail.com> writes: >> On Wed, Jun 12, 2024 at 4:36 PM David G. Johnston < >> david.g.johnston@gmail.com> wrote: >>> I think my point is that a paragraph like the following may be a useful >>> addition: >>> >>> If one wishes to remove the default privilege granted to public to execute >>> all newly created procedures it is necessary to revoke that privilege for >>> every superuser in the system > >> That seems... excessive. > > More to the point, it's wrong. Superusers have every privilege there > is "ex officio"; we don't even bother to look at the catalog entries > when considering a privilege check for a superuser. Revoking their > privileges will accomplish nothing, and it does nothing about the > actual source of the problem (the default grant to PUBLIC) either. > > What I'd do if I didn't like this policy is some variant of > > ALTER DEFAULT PRIVILEGES IN SCHEMA public > REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC; In a past blog[1] I opined that this cleans up the default security posture fairly completely: 8<---------------------- REVOKE CREATE ON SCHEMA public FROM PUBLIC; REVOKE EXECUTE ON ALL ROUTINES IN SCHEMA public FROM PUBLIC; ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE EXECUTE ON ROUTINES FROM PUBLIC; -- And/or possibly, more drastic options: -- REVOKE USAGE ON SCHEMA public FROM PUBLIC; -- DROP SCHEMA public CASCADE; REVOKE TEMPORARY ON DATABASE <your_db> FROM PUBLIC; REVOKE USAGE ON LANGUAGE sql, plpgsql FROM PUBLIC; 8<---------------------- > Repeat for each schema that you think might be publicly readable > (which is only public by default). indeed > BTW, in PG 15 and up, the public schema is not writable by > default, which attacks basically the same problem from a different > direction. also a good point [1] https://www.crunchydata.com/blog/postgresql-defaults-and-impact-on-security-part-2 -- Joe Conway PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com