Skip to content
Permalink
Browse files

Document security implications of search_path and the public schema.

The ability to create like-named objects in different schemas opens up
the potential for users to change the behavior of other users' queries,
maliciously or accidentally.  When you connect to a PostgreSQL server,
you should remove from your search_path any schema for which a user
other than yourself or superusers holds the CREATE privilege.  If you do
not, other users holding CREATE privilege can redefine the behavior of
your commands, causing them to perform arbitrary SQL statements under
your identity.  "SET search_path = ..." and "SELECT
pg_catalog.set_config(...)" are not vulnerable to such hijacking, so one
can use either as the first command of a session.  As special
exceptions, the following client applications behave as documented
regardless of search_path settings and schema privileges: clusterdb
createdb createlang createuser dropdb droplang dropuser ecpg (not
programs it generates) initdb oid2name pg_archivecleanup pg_basebackup
pg_config pg_controldata pg_ctl pg_dump pg_dumpall pg_isready
pg_receivewal pg_recvlogical pg_resetwal pg_restore pg_rewind pg_standby
pg_test_fsync pg_test_timing pg_upgrade pg_waldump reindexdb vacuumdb
vacuumlo.  Not included are core client programs that run user-specified
SQL commands, namely psql and pgbench.  PostgreSQL encourages non-core
client applications to do likewise.

Document this in the context of libpq connections, psql connections,
dblink connections, ECPG connections, extension packaging, and schema
usage patterns.  The principal defense for applications is "SELECT
pg_catalog.set_config('search_path', '', false)", and the principal
defense for databases is "REVOKE CREATE ON SCHEMA public FROM PUBLIC".
Either one is sufficient to prevent attack.  After a REVOKE, consider
auditing the public schema for objects named like pg_catalog objects.

Authors of SECURITY DEFINER functions use some of the same defenses, and
the CREATE FUNCTION reference page already covered them thoroughly.
This is a good opportunity to audit SECURITY DEFINER functions for
robust security practice.

Back-patch to 9.3 (all supported versions).

Reviewed by Michael Paquier and Jonathan S. Katz.  Reported by Arseniy
Sharoglazov.

Security: CVE-2018-1058
  • Loading branch information...
nmisch committed Feb 26, 2018
1 parent 582edc3 commit 5770172cb0c9df9e6ce27c507b449557e5b45124
@@ -6329,6 +6329,13 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
setting, either globally or per-user.
</para>

<para>
For more information on schema handling, see
<xref linkend="ddl-schemas"/>. In particular, the default
configuration is suitable only when the database has a single user or
a few mutually-trusting users.
</para>

<para>
The current effective value of the search path can be examined
via the <acronym>SQL</acronym> function
@@ -6340,9 +6347,6 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
appearing in <varname>search_path</varname> were resolved.
</para>

<para>
For more information on schema handling, see <xref linkend="ddl-schemas"/>.
</para>
</listitem>
</varlistentry>

@@ -75,7 +75,7 @@ CREATE EXTENSION <replaceable>module_name</replaceable>;
choice. To do that, add <literal>SCHEMA
<replaceable>schema_name</replaceable></literal> to the <command>CREATE EXTENSION</command>
command. By default, the objects will be placed in your current creation
target schema, typically <literal>public</literal>.
target schema, which in turn defaults to <literal>public</literal>.
</para>

<para>
@@ -83,7 +83,7 @@ dblink_connect(text connname, text connstr) returns text
<listitem>
<para><application>libpq</application>-style connection info string, for example
<literal>hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres
password=mypasswd</literal>.
password=mypasswd options=-csearch_path=</literal>.
For details see <xref linkend="libpq-connstring"/>.
Alternatively, the name of a foreign server.
</para>
@@ -104,6 +104,17 @@ dblink_connect(text connname, text connstr) returns text
<refsect1>
<title>Notes</title>

<para>
If untrusted users have access to a database that has not adopted a
<link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
begin each session by removing publicly-writable schemas from
<varname>search_path</varname>. One could, for example,
add <literal>options=-csearch_path=</literal> to
<parameter>connstr</parameter>. This consideration is not specific
to <filename>dblink</filename>; it applies to every interface for
executing arbitrary SQL commands.
</para>

<para>
Only superusers may use <function>dblink_connect</function> to create
non-password-authenticated connections. If non-superusers need this
@@ -121,13 +132,13 @@ dblink_connect(text connname, text connstr) returns text
<title>Examples</title>

<screen>
SELECT dblink_connect('dbname=postgres');
SELECT dblink_connect('dbname=postgres options=-csearch_path=');
dblink_connect
----------------
OK
(1 row)

SELECT dblink_connect('myconn', 'dbname=postgres');
SELECT dblink_connect('myconn', 'dbname=postgres options=-csearch_path=');
dblink_connect
----------------
OK
@@ -416,7 +427,8 @@ dblink(text sql [, bool fail_on_error]) returns setof record

<programlisting>
SELECT *
FROM dblink('dbname=mydb', 'select proname, prosrc from pg_proc')
FROM dblink('dbname=mydb options=-csearch_path=',
'select proname, prosrc from pg_proc')
AS t1(proname name, prosrc text)
WHERE proname LIKE 'bytea%';
</programlisting>
@@ -450,7 +462,8 @@ SELECT *
<programlisting>
CREATE VIEW myremote_pg_proc AS
SELECT *
FROM dblink('dbname=postgres', 'select proname, prosrc from pg_proc')
FROM dblink('dbname=postgres options=-csearch_path=',
'select proname, prosrc from pg_proc')
AS t1(proname name, prosrc text);

SELECT * FROM myremote_pg_proc WHERE proname LIKE 'bytea%';
@@ -461,7 +474,8 @@ SELECT * FROM myremote_pg_proc WHERE proname LIKE 'bytea%';
<title>Examples</title>

<screen>
SELECT * FROM dblink('dbname=postgres', 'select proname, prosrc from pg_proc')
SELECT * FROM dblink('dbname=postgres options=-csearch_path=',
'select proname, prosrc from pg_proc')
AS t1(proname name, prosrc text) WHERE proname LIKE 'bytea%';
proname | prosrc
------------+------------
@@ -479,7 +493,7 @@ SELECT * FROM dblink('dbname=postgres', 'select proname, prosrc from pg_proc')
byteaout | byteaout
(12 rows)

SELECT dblink_connect('dbname=postgres');
SELECT dblink_connect('dbname=postgres options=-csearch_path=');
dblink_connect
----------------
OK
@@ -503,7 +517,7 @@ SELECT * FROM dblink('select proname, prosrc from pg_proc')
byteaout | byteaout
(12 rows)

SELECT dblink_connect('myconn', 'dbname=regression');
SELECT dblink_connect('myconn', 'dbname=regression options=-csearch_path=');
dblink_connect
----------------
OK
@@ -778,7 +792,7 @@ dblink_open(text connname, text cursorname, text sql [, bool fail_on_error]) ret
<title>Examples</title>

<screen>
SELECT dblink_connect('dbname=postgres');
SELECT dblink_connect('dbname=postgres options=-csearch_path=');
dblink_connect
----------------
OK
@@ -899,7 +913,7 @@ dblink_fetch(text connname, text cursorname, int howmany [, bool fail_on_error])
<title>Examples</title>

<screen>
SELECT dblink_connect('dbname=postgres');
SELECT dblink_connect('dbname=postgres options=-csearch_path=');
dblink_connect
----------------
OK
@@ -1036,7 +1050,7 @@ dblink_close(text connname, text cursorname [, bool fail_on_error]) returns text
<title>Examples</title>

<screen>
SELECT dblink_connect('dbname=postgres');
SELECT dblink_connect('dbname=postgres options=-csearch_path=');
dblink_connect
----------------
OK
@@ -2172,6 +2172,20 @@ CREATE TABLE public.products ( ... );
in other schemas in the database.
</para>

<para>
The ability to create like-named objects in different schemas complicates
writing a query that references precisely the same objects every time. It
also opens up the potential for users to change the behavior of other
users' queries, maliciously or accidentally. Due to the prevalence of
unqualified names in queries and their use
in <productname>PostgreSQL</productname> internals, adding a schema
to <varname>search_path</varname> effectively trusts all users having
<literal>CREATE</literal> privilege on that schema. When you run an
ordinary query, a malicious user able to create objects in a schema of
your search path can take control and execute arbitrary SQL functions as
though you executed them.
</para>

<indexterm>
<primary>schema</primary>
<secondary>current</secondary>
@@ -2288,8 +2302,9 @@ SELECT 3 OPERATOR(pg_catalog.+) 4;
the schema
<literal>public</literal>. This allows all users that are able to
connect to a given database to create objects in its
<literal>public</literal> schema. If you do
not want to allow that, you can revoke that privilege:
<literal>public</literal> schema.
Some <link linkend="ddl-schemas-patterns">usage patterns</link> call for
revoking that privilege:
<programlisting>
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
</programlisting>
@@ -2339,50 +2354,75 @@ REVOKE CREATE ON SCHEMA public FROM PUBLIC;
<title>Usage Patterns</title>

<para>
Schemas can be used to organize your data in many ways. There are
a few usage patterns that are recommended and are easily supported by
the default configuration:
Schemas can be used to organize your data in many ways. There are a few
usage patterns easily supported by the default configuration, only one of
which suffices when database users mistrust other database users:
<itemizedlist>
<listitem>
<!-- "DROP SCHEMA public" is inferior to this REVOKE, because pg_dump
doesn't preserve that DROP. -->
<para>
If you do not create any schemas then all users access the
public schema implicitly. This simulates the situation where
schemas are not available at all. This setup is mainly
recommended when there is only a single user or a few cooperating
users in a database. This setup also allows smooth transition
from the non-schema-aware world.
Constrain ordinary users to user-private schemas. To implement this,
issue <literal>REVOKE CREATE ON SCHEMA public FROM PUBLIC</literal>,
and create a schema for each user with the same name as that user. If
affected users had logged in before this, consider auditing the public
schema for objects named like objects in
schema <literal>pg_catalog</literal>. Recall that the default search
path starts with <literal>$user</literal>, which resolves to the user
name. Therefore, if each user has a separate schema, they access their
own schemas by default.
</para>
</listitem>

<listitem>
<para>
You can create a schema for each user with the same name as
that user. Recall that the default search path starts with
<literal>$user</literal>, which resolves to the user name.
Therefore, if each user has a separate schema, they access their
own schemas by default.
Remove the public schema from each user's default search path
using <literal>ALTER ROLE <replaceable>user</replaceable> SET
search_path = "$user"</literal>. Everyone retains the ability to
create objects in the public schema, but only qualified names will
choose those objects. A user holding the <literal>CREATEROLE</literal>
privilege can undo this setting and issue arbitrary queries under the
identity of users relying on the setting. If you
grant <literal>CREATEROLE</literal> to users not warranting this
almost-superuser ability, use the first pattern instead.
</para>
</listitem>

<listitem>
<para>
If you use this setup then you might also want to revoke access
to the public schema (or drop it altogether), so users are
truly constrained to their own schemas.
Remove the public schema from <varname>search_path</varname> in
<link linkend="config-setting-configuration-file"><filename>postgresql.conf</filename></link>.
The ensuing user experience matches the previous pattern. In addition
to that pattern's implications for <literal>CREATEROLE</literal>, this
trusts database owners the same way. If you assign
the <literal>CREATEROLE</literal>
privilege, <literal>CREATEDB</literal> privilege or individual database
ownership to users not warranting almost-superuser access, use the
first pattern instead.
</para>
</listitem>

<listitem>
<para>
To install shared applications (tables to be used by everyone,
additional functions provided by third parties, etc.), put them
into separate schemas. Remember to grant appropriate
privileges to allow the other users to access them. Users can
then refer to these additional objects by qualifying the names
with a schema name, or they can put the additional schemas into
their search path, as they choose.
Keep the default. All users access the public schema implicitly. This
simulates the situation where schemas are not available at all, giving
a smooth transition from the non-schema-aware world. However, any user
can issue arbitrary queries under the identity of any user not electing
to protect itself individually. This pattern is acceptable only when
the database has a single user or a few mutually-trusting users.
</para>
</listitem>
</itemizedlist>
</para>

<para>
For any pattern, to install shared applications (tables to be used by
everyone, additional functions provided by third parties, etc.), put them
into separate schemas. Remember to grant appropriate privileges to allow
the other users to access them. Users can then refer to these additional
objects by qualifying the names with a schema name, or they can put the
additional schemas into their search path, as they choose.
</para>
</sect2>

<sect2 id="ddl-schemas-portability">
@@ -2405,7 +2445,7 @@ REVOKE CREATE ON SCHEMA public FROM PUBLIC;
<para>
Also, there is no concept of a <literal>public</literal> schema in the
SQL standard. For maximum conformance to the standard, you should
not use (perhaps even remove) the <literal>public</literal> schema.
not use the <literal>public</literal> schema.
</para>

<para>

0 comments on commit 5770172

Please sign in to comment.
You can’t perform that action at this time.