Skip to content

Commit 5770172

Browse files
committedFeb 26, 2018
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
1 parent 582edc3 commit 5770172

19 files changed

+369
-100
lines changed
 

‎doc/src/sgml/config.sgml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6329,6 +6329,13 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
63296329
setting, either globally or per-user.
63306330
</para>
63316331

6332+
<para>
6333+
For more information on schema handling, see
6334+
<xref linkend="ddl-schemas"/>. In particular, the default
6335+
configuration is suitable only when the database has a single user or
6336+
a few mutually-trusting users.
6337+
</para>
6338+
63326339
<para>
63336340
The current effective value of the search path can be examined
63346341
via the <acronym>SQL</acronym> function
@@ -6340,9 +6347,6 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
63406347
appearing in <varname>search_path</varname> were resolved.
63416348
</para>
63426349

6343-
<para>
6344-
For more information on schema handling, see <xref linkend="ddl-schemas"/>.
6345-
</para>
63466350
</listitem>
63476351
</varlistentry>
63486352

‎doc/src/sgml/contrib.sgml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ CREATE EXTENSION <replaceable>module_name</replaceable>;
7575
choice. To do that, add <literal>SCHEMA
7676
<replaceable>schema_name</replaceable></literal> to the <command>CREATE EXTENSION</command>
7777
command. By default, the objects will be placed in your current creation
78-
target schema, typically <literal>public</literal>.
78+
target schema, which in turn defaults to <literal>public</literal>.
7979
</para>
8080

8181
<para>

‎doc/src/sgml/dblink.sgml

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ dblink_connect(text connname, text connstr) returns text
8383
<listitem>
8484
<para><application>libpq</application>-style connection info string, for example
8585
<literal>hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres
86-
password=mypasswd</literal>.
86+
password=mypasswd options=-csearch_path=</literal>.
8787
For details see <xref linkend="libpq-connstring"/>.
8888
Alternatively, the name of a foreign server.
8989
</para>
@@ -104,6 +104,17 @@ dblink_connect(text connname, text connstr) returns text
104104
<refsect1>
105105
<title>Notes</title>
106106

107+
<para>
108+
If untrusted users have access to a database that has not adopted a
109+
<link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
110+
begin each session by removing publicly-writable schemas from
111+
<varname>search_path</varname>. One could, for example,
112+
add <literal>options=-csearch_path=</literal> to
113+
<parameter>connstr</parameter>. This consideration is not specific
114+
to <filename>dblink</filename>; it applies to every interface for
115+
executing arbitrary SQL commands.
116+
</para>
117+
107118
<para>
108119
Only superusers may use <function>dblink_connect</function> to create
109120
non-password-authenticated connections. If non-superusers need this
@@ -121,13 +132,13 @@ dblink_connect(text connname, text connstr) returns text
121132
<title>Examples</title>
122133

123134
<screen>
124-
SELECT dblink_connect('dbname=postgres');
135+
SELECT dblink_connect('dbname=postgres options=-csearch_path=');
125136
dblink_connect
126137
----------------
127138
OK
128139
(1 row)
129140

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

417428
<programlisting>
418429
SELECT *
419-
FROM dblink('dbname=mydb', 'select proname, prosrc from pg_proc')
430+
FROM dblink('dbname=mydb options=-csearch_path=',
431+
'select proname, prosrc from pg_proc')
420432
AS t1(proname name, prosrc text)
421433
WHERE proname LIKE 'bytea%';
422434
</programlisting>
@@ -450,7 +462,8 @@ SELECT *
450462
<programlisting>
451463
CREATE VIEW myremote_pg_proc AS
452464
SELECT *
453-
FROM dblink('dbname=postgres', 'select proname, prosrc from pg_proc')
465+
FROM dblink('dbname=postgres options=-csearch_path=',
466+
'select proname, prosrc from pg_proc')
454467
AS t1(proname name, prosrc text);
455468

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

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

482-
SELECT dblink_connect('dbname=postgres');
496+
SELECT dblink_connect('dbname=postgres options=-csearch_path=');
483497
dblink_connect
484498
----------------
485499
OK
@@ -503,7 +517,7 @@ SELECT * FROM dblink('select proname, prosrc from pg_proc')
503517
byteaout | byteaout
504518
(12 rows)
505519

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

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

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

10381052
<screen>
1039-
SELECT dblink_connect('dbname=postgres');
1053+
SELECT dblink_connect('dbname=postgres options=-csearch_path=');
10401054
dblink_connect
10411055
----------------
10421056
OK

‎doc/src/sgml/ddl.sgml

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2172,6 +2172,20 @@ CREATE TABLE public.products ( ... );
21722172
in other schemas in the database.
21732173
</para>
21742174

2175+
<para>
2176+
The ability to create like-named objects in different schemas complicates
2177+
writing a query that references precisely the same objects every time. It
2178+
also opens up the potential for users to change the behavior of other
2179+
users' queries, maliciously or accidentally. Due to the prevalence of
2180+
unqualified names in queries and their use
2181+
in <productname>PostgreSQL</productname> internals, adding a schema
2182+
to <varname>search_path</varname> effectively trusts all users having
2183+
<literal>CREATE</literal> privilege on that schema. When you run an
2184+
ordinary query, a malicious user able to create objects in a schema of
2185+
your search path can take control and execute arbitrary SQL functions as
2186+
though you executed them.
2187+
</para>
2188+
21752189
<indexterm>
21762190
<primary>schema</primary>
21772191
<secondary>current</secondary>
@@ -2288,8 +2302,9 @@ SELECT 3 OPERATOR(pg_catalog.+) 4;
22882302
the schema
22892303
<literal>public</literal>. This allows all users that are able to
22902304
connect to a given database to create objects in its
2291-
<literal>public</literal> schema. If you do
2292-
not want to allow that, you can revoke that privilege:
2305+
<literal>public</literal> schema.
2306+
Some <link linkend="ddl-schemas-patterns">usage patterns</link> call for
2307+
revoking that privilege:
22932308
<programlisting>
22942309
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
22952310
</programlisting>
@@ -2339,50 +2354,75 @@ REVOKE CREATE ON SCHEMA public FROM PUBLIC;
23392354
<title>Usage Patterns</title>
23402355

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

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

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

23732405
<listitem>
23742406
<para>
2375-
To install shared applications (tables to be used by everyone,
2376-
additional functions provided by third parties, etc.), put them
2377-
into separate schemas. Remember to grant appropriate
2378-
privileges to allow the other users to access them. Users can
2379-
then refer to these additional objects by qualifying the names
2380-
with a schema name, or they can put the additional schemas into
2381-
their search path, as they choose.
2407+
Keep the default. All users access the public schema implicitly. This
2408+
simulates the situation where schemas are not available at all, giving
2409+
a smooth transition from the non-schema-aware world. However, any user
2410+
can issue arbitrary queries under the identity of any user not electing
2411+
to protect itself individually. This pattern is acceptable only when
2412+
the database has a single user or a few mutually-trusting users.
23822413
</para>
23832414
</listitem>
23842415
</itemizedlist>
23852416
</para>
2417+
2418+
<para>
2419+
For any pattern, to install shared applications (tables to be used by
2420+
everyone, additional functions provided by third parties, etc.), put them
2421+
into separate schemas. Remember to grant appropriate privileges to allow
2422+
the other users to access them. Users can then refer to these additional
2423+
objects by qualifying the names with a schema name, or they can put the
2424+
additional schemas into their search path, as they choose.
2425+
</para>
23862426
</sect2>
23872427

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

24112451
<para>

‎doc/src/sgml/ecpg.sgml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,18 @@ EXEC SQL CONNECT TO <replaceable>target</replaceable> <optional>AS <replaceable>
186186
chapter).
187187
</para>
188188

189+
<para>
190+
If untrusted users have access to a database that has not adopted a
191+
<link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
192+
begin each session by removing publicly-writable schemas
193+
from <varname>search_path</varname>. For example,
194+
add <literal>options=-csearch_path=</literal>
195+
to <literal><replaceable>options</replaceable></literal>, or
196+
issue <literal>EXEC SQL SELECT pg_catalog.set_config('search_path', '',
197+
false);</literal> after connecting. This consideration is not specific to
198+
ECPG; it applies to every interface for executing arbitrary SQL commands.
199+
</para>
200+
189201
<para>
190202
Here are some examples of <command>CONNECT</command> statements:
191203
<programlisting>
@@ -266,8 +278,11 @@ int
266278
main()
267279
{
268280
EXEC SQL CONNECT TO testdb1 AS con1 USER testuser;
281+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
269282
EXEC SQL CONNECT TO testdb2 AS con2 USER testuser;
283+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
270284
EXEC SQL CONNECT TO testdb3 AS con3 USER testuser;
285+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
271286

272287
/* This query would be executed in the last opened database "testdb3". */
273288
EXEC SQL SELECT current_database() INTO :dbname;
@@ -1093,6 +1108,7 @@ EXEC SQL BEGIN DECLARE SECTION;
10931108
EXEC SQL END DECLARE SECTION;
10941109

10951110
EXEC SQL CONNECT TO testdb;
1111+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
10961112

10971113
in = PGTYPESinterval_new();
10981114
EXEC SQL SELECT '1 min'::interval INTO :in;
@@ -1147,6 +1163,7 @@ EXEC SQL BEGIN DECLARE SECTION;
11471163
EXEC SQL END DECLARE SECTION;
11481164

11491165
EXEC SQL CONNECT TO testdb;
1166+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
11501167

11511168
num = PGTYPESnumeric_new();
11521169
dec = PGTYPESdecimal_new();
@@ -1221,6 +1238,7 @@ EXEC SQL END DECLARE SECTION;
12211238
memset(dbid, 0, sizeof(int) * 8);
12221239

12231240
EXEC SQL CONNECT TO testdb;
1241+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
12241242

12251243
/* Retrieve multiple rows into arrays at once. */
12261244
EXEC SQL SELECT oid,datname INTO :dbid, :dbname FROM pg_database;
@@ -1887,6 +1905,7 @@ char *stmt = "SELECT u.usename as dbaname, d.datname "
18871905
EXEC SQL END DECLARE SECTION;
18881906

18891907
EXEC SQL CONNECT TO testdb AS con1 USER testuser;
1908+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
18901909

18911910
EXEC SQL PREPARE stmt1 FROM :stmt;
18921911

@@ -4317,6 +4336,7 @@ main(void)
43174336
EXEC SQL END DECLARE SECTION;
43184337

43194338
EXEC SQL CONNECT TO testdb AS con1 USER testuser;
4339+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
43204340

43214341
EXEC SQL PREPARE stmt1 FROM :query;
43224342
EXEC SQL DECLARE cur1 CURSOR FOR stmt1;
@@ -4478,6 +4498,7 @@ main(void)
44784498
EXEC SQL END DECLARE SECTION;
44794499

44804500
EXEC SQL CONNECT TO uptimedb AS con1 USER uptime;
4501+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
44814502

44824503
EXEC SQL PREPARE stmt1 FROM :query;
44834504
EXEC SQL DECLARE cur1 CURSOR FOR stmt1;
@@ -5909,6 +5930,7 @@ main(void)
59095930
memset(buf, 1, buflen);
59105931

59115932
EXEC SQL CONNECT TO testdb AS con1;
5933+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
59125934

59135935
conn = ECPGget_PGconn("con1");
59145936
printf("conn = %p\n", conn);
@@ -6038,6 +6060,7 @@ class TestCpp
60386060
TestCpp::TestCpp()
60396061
{
60406062
EXEC SQL CONNECT TO testdb1;
6063+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
60416064
}
60426065

60436066
void Test::test()
@@ -6117,6 +6140,7 @@ void
61176140
db_connect()
61186141
{
61196142
EXEC SQL CONNECT TO testdb1;
6143+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
61206144
}
61216145

61226146
void
@@ -6510,12 +6534,14 @@ EXEC SQL END DECLARE SECTION;
65106534
ECPGdebug(1, stderr);
65116535

65126536
EXEC SQL CONNECT TO :dbname USER :user;
6537+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
65136538
EXEC SQL SELECT version() INTO :ver;
65146539
EXEC SQL DISCONNECT;
65156540

65166541
printf("version: %s\n", ver);
65176542

65186543
EXEC SQL CONNECT TO :connection USER :user;
6544+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
65196545
EXEC SQL SELECT version() INTO :ver;
65206546
EXEC SQL DISCONNECT;
65216547

@@ -7116,6 +7142,7 @@ EXEC SQL BEGIN DECLARE SECTION;
71167142
EXEC SQL END DECLARE SECTION;
71177143

71187144
EXEC SQL CONNECT TO testdb AS con1 USER testuser;
7145+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
71197146
EXEC SQL ALLOCATE DESCRIPTOR d;
71207147

71217148
/* Declare, open a cursor, and assign a descriptor to the cursor */
@@ -7673,6 +7700,7 @@ EXEC SQL BEGIN DECLARE SECTION;
76737700
EXEC SQL END DECLARE SECTION;
76747701

76757702
EXEC SQL CONNECT TO testdb AS con1;
7703+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
76767704

76777705
EXEC SQL SELECT current_database(), 256 INTO :t:t_ind LIMIT 1;
76787706

@@ -7829,6 +7857,7 @@ int
78297857
main(void)
78307858
{
78317859
EXEC SQL CONNECT TO testdb AS con1;
7860+
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;
78327861
EXEC SQL ALLOCATE DESCRIPTOR d;
78337862
EXEC SQL DECLARE cur CURSOR FOR SELECT current_database(), 'hoge', 256;
78347863
EXEC SQL OPEN cur;

‎doc/src/sgml/extend.sgml

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,32 @@
430430
dropping the whole extension.
431431
</para>
432432

433+
<sect2 id="extend-extensions-style">
434+
<title>Defining Extension Objects</title>
435+
436+
<!-- XXX It's not enough to use qualified names, because one might write a
437+
qualified name to an object that itself uses unqualified names. Many
438+
information_schema functions have that defect, for example. However,
439+
that's a defect in the referenced object, and relatively few queries
440+
will be affected. Also, we direct applications to secure search_path
441+
when connecting to an untrusted database; if applications do that,
442+
they are immune to known attacks even if some extension refers to a
443+
defective object. Therefore, guide extension authors as though core
444+
PostgreSQL contained no such defect. -->
445+
<para>
446+
Widely-distributed extensions should assume little about the database
447+
they occupy. In particular, unless you issued <literal>SET search_path =
448+
pg_temp</literal>, assume each unqualified name could resolve to an
449+
object that a malicious user has defined. Beware of constructs that
450+
depend on <varname>search_path</varname> implicitly: <token>IN</token>
451+
and <literal>CASE <replaceable>expression</replaceable> WHEN</literal>
452+
always select an operator using the search path. In their place, use
453+
<literal>OPERATOR(<replaceable>schema</replaceable>.=) ANY</literal>
454+
and <literal>CASE WHEN <replaceable>expression</replaceable></literal>.
455+
</para>
456+
457+
</sect2>
458+
433459
<sect2>
434460
<title>Extension Files</title>
435461

@@ -984,24 +1010,24 @@ SELECT * FROM pg_extension_update_paths('<replaceable>extension_name</replaceabl
9841010
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
9851011
\echo Use "CREATE EXTENSION pair" to load this file. \quit
9861012

987-
CREATE TYPE pair AS ( k text, v text );
988-
989-
CREATE OR REPLACE FUNCTION pair(anyelement, text)
990-
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
1013+
CREATE TYPE pair AS ( k pg_catalog.text, v pg_catalog.text );
9911014

992-
CREATE OR REPLACE FUNCTION pair(text, anyelement)
993-
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
1015+
CREATE OR REPLACE FUNCTION pair(pg_catalog.text, pg_catalog.text)
1016+
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::@extschema@.pair;';
9941017

995-
CREATE OR REPLACE FUNCTION pair(anyelement, anyelement)
996-
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
1018+
CREATE OPERATOR ~> (LEFTARG = pg_catalog.text,
1019+
RIGHTARG = pg_catalog.text, PROCEDURE = pair);
9971020

998-
CREATE OR REPLACE FUNCTION pair(text, text)
999-
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair;';
1021+
-- "SET search_path" is easy to get right, but qualified names perform better.
1022+
CREATE OR REPLACE FUNCTION lower(pair)
1023+
RETURNS pair LANGUAGE SQL
1024+
AS 'SELECT ROW(lower($1.k), lower($1.v))::@extschema@.pair;'
1025+
SET search_path = pg_temp;
10001026

1001-
CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = anyelement, PROCEDURE = pair);
1002-
CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = text, PROCEDURE = pair);
1003-
CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = anyelement, PROCEDURE = pair);
1004-
CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair);
1027+
CREATE OR REPLACE FUNCTION pair_concat(pair, pair)
1028+
RETURNS pair LANGUAGE SQL
1029+
AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k,
1030+
$1.v OPERATOR(pg_catalog.||) $2.v)::@extschema@.pair;';
10051031
]]>
10061032
</programlisting>
10071033
</para>
@@ -1013,7 +1039,7 @@ CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair);
10131039
# pair extension
10141040
comment = 'A key/value pair data type'
10151041
default_version = '1.0'
1016-
relocatable = true
1042+
relocatable = false
10171043
</programlisting>
10181044
</para>
10191045

‎doc/src/sgml/libpq.sgml

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,22 @@
6565
the return value for a successful connection before queries are sent
6666
via the connection object.
6767

68+
<warning>
69+
<para>
70+
If untrusted users have access to a database that has not adopted a
71+
<link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
72+
begin each session by removing publicly-writable schemas from
73+
<varname>search_path</varname>. One can set parameter key
74+
word <literal>options</literal> to
75+
value <literal>-csearch_path=</literal>. Alternately, one can
76+
issue <literal>PQexec(<replaceable>conn</replaceable>, "SELECT
77+
pg_catalog.set_config('search_path', '', false)")</literal> after
78+
connecting. This consideration is not specific
79+
to <application>libpq</application>; it applies to every interface for
80+
executing arbitrary SQL commands.
81+
</para>
82+
</warning>
83+
6884
<warning>
6985
<para>
7086
On Unix, forking a process with open libpq connections can lead to
@@ -6878,7 +6894,8 @@ main(void)
68786894
{
68796895
mydata *data;
68806896
PGresult *res;
6881-
PGconn *conn = PQconnectdb("dbname = postgres");
6897+
PGconn *conn =
6898+
PQconnectdb("dbname=postgres options=-csearch_path=");
68826899

68836900
if (PQstatus(conn) != CONNECTION_OK)
68846901
{
@@ -8305,6 +8322,22 @@ main(int argc, char **argv)
83058322
exit_nicely(conn);
83068323
}
83078324

8325+
/* Set always-secure search path, so malicous users can't take control. */
8326+
res = PQexec(conn,
8327+
"SELECT pg_catalog.set_config('search_path', '', false)");
8328+
if (PQresultStatus(res) != PGRES_COMMAND_OK)
8329+
{
8330+
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
8331+
PQclear(res);
8332+
exit_nicely(conn);
8333+
}
8334+
8335+
/*
8336+
* Should PQclear PGresult whenever it is no longer needed to avoid memory
8337+
* leaks
8338+
*/
8339+
PQclear(res);
8340+
83088341
/*
83098342
* Our test case here involves using a cursor, for which we must be inside
83108343
* a transaction block. We could do the whole thing with a single
@@ -8320,11 +8353,6 @@ main(int argc, char **argv)
83208353
PQclear(res);
83218354
exit_nicely(conn);
83228355
}
8323-
8324-
/*
8325-
* Should PQclear PGresult whenever it is no longer needed to avoid memory
8326-
* leaks
8327-
*/
83288356
PQclear(res);
83298357

83308358
/*
@@ -8400,16 +8428,16 @@ main(int argc, char **argv)
84008428
* populate a database with the following commands
84018429
* (provided in src/test/examples/testlibpq2.sql):
84028430
*
8431+
* CREATE SCHEMA TESTLIBPQ2;
8432+
* SET search_path = TESTLIBPQ2;
84038433
* CREATE TABLE TBL1 (i int4);
8404-
*
84058434
* CREATE TABLE TBL2 (i int4);
8406-
*
84078435
* CREATE RULE r1 AS ON INSERT TO TBL1 DO
84088436
* (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);
84098437
*
8410-
* and do this four times:
8438+
* Start this program, then from psql do this four times:
84118439
*
8412-
* INSERT INTO TBL1 VALUES (10);
8440+
* INSERT INTO TESTLIBPQ2.TBL1 VALUES (10);
84138441
*/
84148442

84158443
#ifdef WIN32
@@ -8464,6 +8492,22 @@ main(int argc, char **argv)
84648492
exit_nicely(conn);
84658493
}
84668494

8495+
/* Set always-secure search path, so malicous users can't take control. */
8496+
res = PQexec(conn,
8497+
"SELECT pg_catalog.set_config('search_path', '', false)");
8498+
if (PQresultStatus(res) != PGRES_COMMAND_OK)
8499+
{
8500+
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
8501+
PQclear(res);
8502+
exit_nicely(conn);
8503+
}
8504+
8505+
/*
8506+
* Should PQclear PGresult whenever it is no longer needed to avoid memory
8507+
* leaks
8508+
*/
8509+
PQclear(res);
8510+
84678511
/*
84688512
* Issue LISTEN command to enable notifications from the rule's NOTIFY.
84698513
*/
@@ -8474,11 +8518,6 @@ main(int argc, char **argv)
84748518
PQclear(res);
84758519
exit_nicely(conn);
84768520
}
8477-
8478-
/*
8479-
* should PQclear PGresult whenever it is no longer needed to avoid memory
8480-
* leaks
8481-
*/
84828521
PQclear(res);
84838522

84848523
/* Quit after four notifies are received. */
@@ -8545,8 +8584,9 @@ main(int argc, char **argv)
85458584
* Before running this, populate a database with the following commands
85468585
* (provided in src/test/examples/testlibpq3.sql):
85478586
*
8587+
* CREATE SCHEMA testlibpq3;
8588+
* SET search_path = testlibpq3;
85488589
* CREATE TABLE test1 (i int4, t text, b bytea);
8549-
*
85508590
* INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
85518591
* INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');
85528592
*
@@ -8678,6 +8718,16 @@ main(int argc, char **argv)
86788718
exit_nicely(conn);
86798719
}
86808720

8721+
/* Set always-secure search path, so malicous users can't take control. */
8722+
res = PQexec(conn, "SET search_path = testlibpq3");
8723+
if (PQresultStatus(res) != PGRES_COMMAND_OK)
8724+
{
8725+
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
8726+
PQclear(res);
8727+
exit_nicely(conn);
8728+
}
8729+
PQclear(res);
8730+
86818731
/*
86828732
* The point of this program is to illustrate use of PQexecParams() with
86838733
* out-of-line parameters, as well as binary transmission of data.

‎doc/src/sgml/lobj.sgml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,17 @@ main(int argc, char **argv)
933933
exit_nicely(conn);
934934
}
935935

936+
/* Set always-secure search path, so malicous users can't take control. */
937+
res = PQexec(conn,
938+
"SELECT pg_catalog.set_config('search_path', '', false)");
939+
if (PQresultStatus(res) != PGRES_COMMAND_OK)
940+
{
941+
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
942+
PQclear(res);
943+
exit_nicely(conn);
944+
}
945+
PQclear(res);
946+
936947
res = PQexec(conn, "begin");
937948
PQclear(res);
938949
printf("importing file \"%s\" ...\n", in_filename);

‎doc/src/sgml/ref/pgbench.sgml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,5 +1688,16 @@ statement latencies in milliseconds:
16881688
database server.
16891689
</para>
16901690
</refsect2>
1691+
<refsect2>
1692+
<title>Security</title>
1693+
1694+
<para>
1695+
If untrusted users have access to a database that has not adopted a
1696+
<link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
1697+
do not run <application>pgbench</application> in that
1698+
database. <application>pgbench</application> uses unqualified names and
1699+
does not manipulate the search path.
1700+
</para>
1701+
</refsect2>
16911702
</refsect1>
16921703
</refentry>

‎doc/src/sgml/ref/psql-ref.sgml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,18 @@ testdb=&gt;
735735
of the command are displayed on the screen.
736736
</para>
737737

738+
<para>
739+
If untrusted users have access to a database that has not adopted a
740+
<link linkend="ddl-schemas-patterns">secure schema usage pattern</link>,
741+
begin your session by removing publicly-writable schemas
742+
from <varname>search_path</varname>. One can
743+
add <literal>options=-csearch_path=</literal> to the connection string or
744+
issue <literal>SELECT pg_catalog.set_config('search_path', '',
745+
false)</literal> before other SQL commands. This consideration is not
746+
specific to <application>psql</application>; it applies to every interface
747+
for executing arbitrary SQL commands.
748+
</para>
749+
738750
<para>
739751
Whenever a command is executed, <application>psql</application> also polls
740752
for asynchronous notification events generated by

‎doc/src/sgml/user-manag.sgml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -571,14 +571,17 @@ GRANT pg_signal_backend TO admin_user;
571571
</sect1>
572572

573573
<sect1 id="perm-functions">
574-
<title>Function and Trigger Security</title>
574+
<title>Function Security</title>
575575

576576
<para>
577-
Functions and triggers allow users to insert code into the backend
578-
server that other users might execute unintentionally. Hence, both
579-
mechanisms permit users to <quote>Trojan horse</quote>
580-
others with relative ease. The only real protection is tight
581-
control over who can define functions.
577+
Functions, triggers and row-level security policies allow users to insert
578+
code into the backend server that other users might execute
579+
unintentionally. Hence, these mechanisms permit users to <quote>Trojan
580+
horse</quote> others with relative ease. The strongest protection is tight
581+
control over who can define objects. Where that is infeasible, write
582+
queries referring only to objects having trusted owners. Remove
583+
from <varname>search_path</varname> the public schema and any other schemas
584+
that permit untrusted users to create objects.
582585
</para>
583586

584587
<para>

‎src/test/examples/testlibpq.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,22 @@ main(int argc, char **argv)
4848
exit_nicely(conn);
4949
}
5050

51+
/* Set always-secure search path, so malicous users can't take control. */
52+
res = PQexec(conn,
53+
"SELECT pg_catalog.set_config('search_path', '', false)");
54+
if (PQresultStatus(res) != PGRES_COMMAND_OK)
55+
{
56+
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
57+
PQclear(res);
58+
exit_nicely(conn);
59+
}
60+
61+
/*
62+
* Should PQclear PGresult whenever it is no longer needed to avoid memory
63+
* leaks
64+
*/
65+
PQclear(res);
66+
5167
/*
5268
* Our test case here involves using a cursor, for which we must be inside
5369
* a transaction block. We could do the whole thing with a single
@@ -63,11 +79,6 @@ main(int argc, char **argv)
6379
PQclear(res);
6480
exit_nicely(conn);
6581
}
66-
67-
/*
68-
* Should PQclear PGresult whenever it is no longer needed to avoid memory
69-
* leaks
70-
*/
7182
PQclear(res);
7283

7384
/*

‎src/test/examples/testlibpq2.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@
1313
* populate a database with the following commands
1414
* (provided in src/test/examples/testlibpq2.sql):
1515
*
16+
* CREATE SCHEMA TESTLIBPQ2;
17+
* SET search_path = TESTLIBPQ2;
1618
* CREATE TABLE TBL1 (i int4);
17-
*
1819
* CREATE TABLE TBL2 (i int4);
19-
*
2020
* CREATE RULE r1 AS ON INSERT TO TBL1 DO
2121
* (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);
2222
*
23-
* and do this four times:
23+
* Start this program, then from psql do this four times:
2424
*
25-
* INSERT INTO TBL1 VALUES (10);
25+
* INSERT INTO TESTLIBPQ2.TBL1 VALUES (10);
2626
*/
2727

2828
#ifdef WIN32
@@ -77,6 +77,22 @@ main(int argc, char **argv)
7777
exit_nicely(conn);
7878
}
7979

80+
/* Set always-secure search path, so malicous users can't take control. */
81+
res = PQexec(conn,
82+
"SELECT pg_catalog.set_config('search_path', '', false)");
83+
if (PQresultStatus(res) != PGRES_COMMAND_OK)
84+
{
85+
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
86+
PQclear(res);
87+
exit_nicely(conn);
88+
}
89+
90+
/*
91+
* Should PQclear PGresult whenever it is no longer needed to avoid memory
92+
* leaks
93+
*/
94+
PQclear(res);
95+
8096
/*
8197
* Issue LISTEN command to enable notifications from the rule's NOTIFY.
8298
*/
@@ -87,11 +103,6 @@ main(int argc, char **argv)
87103
PQclear(res);
88104
exit_nicely(conn);
89105
}
90-
91-
/*
92-
* should PQclear PGresult whenever it is no longer needed to avoid memory
93-
* leaks
94-
*/
95106
PQclear(res);
96107

97108
/* Quit after four notifies are received. */

‎src/test/examples/testlibpq2.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
CREATE SCHEMA TESTLIBPQ2;
2+
SET search_path = TESTLIBPQ2;
13
CREATE TABLE TBL1 (i int4);
2-
34
CREATE TABLE TBL2 (i int4);
4-
55
CREATE RULE r1 AS ON INSERT TO TBL1 DO
66
(INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);

‎src/test/examples/testlibpq3.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
* Before running this, populate a database with the following commands
99
* (provided in src/test/examples/testlibpq3.sql):
1010
*
11+
* CREATE SCHEMA testlibpq3;
12+
* SET search_path = testlibpq3;
1113
* CREATE TABLE test1 (i int4, t text, b bytea);
12-
*
1314
* INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
1415
* INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');
1516
*
@@ -141,6 +142,16 @@ main(int argc, char **argv)
141142
exit_nicely(conn);
142143
}
143144

145+
/* Set always-secure search path, so malicous users can't take control. */
146+
res = PQexec(conn, "SET search_path = testlibpq3");
147+
if (PQresultStatus(res) != PGRES_COMMAND_OK)
148+
{
149+
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
150+
PQclear(res);
151+
exit_nicely(conn);
152+
}
153+
PQclear(res);
154+
144155
/*
145156
* The point of this program is to illustrate use of PQexecParams() with
146157
* out-of-line parameters, as well as binary transmission of data.

‎src/test/examples/testlibpq3.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1+
CREATE SCHEMA testlibpq3;
2+
SET search_path = testlibpq3;
13
CREATE TABLE test1 (i int4, t text, b bytea);
2-
34
INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
45
INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');

‎src/test/examples/testlibpq4.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,28 @@ exit_nicely(PGconn *conn1, PGconn *conn2)
2222
}
2323

2424
static void
25-
check_conn(PGconn *conn, const char *dbName)
25+
check_prepare_conn(PGconn *conn, const char *dbName)
2626
{
27+
PGresult *res;
28+
2729
/* check to see that the backend connection was successfully made */
2830
if (PQstatus(conn) != CONNECTION_OK)
2931
{
3032
fprintf(stderr, "Connection to database \"%s\" failed: %s",
3133
dbName, PQerrorMessage(conn));
3234
exit(1);
3335
}
36+
37+
/* Set always-secure search path, so malicous users can't take control. */
38+
res = PQexec(conn,
39+
"SELECT pg_catalog.set_config('search_path', '', false)");
40+
if (PQresultStatus(res) != PGRES_COMMAND_OK)
41+
{
42+
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
43+
PQclear(res);
44+
exit(1);
45+
}
46+
PQclear(res);
3447
}
3548

3649
int
@@ -80,10 +93,10 @@ main(int argc, char **argv)
8093

8194
/* make a connection to the database */
8295
conn1 = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName1);
83-
check_conn(conn1, dbName1);
96+
check_prepare_conn(conn1, dbName1);
8497

8598
conn2 = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName2);
86-
check_conn(conn2, dbName2);
99+
check_prepare_conn(conn2, dbName2);
87100

88101
/* start a transaction block */
89102
res1 = PQexec(conn1, "BEGIN");

‎src/test/examples/testlo.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,17 @@ main(int argc, char **argv)
232232
exit_nicely(conn);
233233
}
234234

235+
/* Set always-secure search path, so malicous users can't take control. */
236+
res = PQexec(conn,
237+
"SELECT pg_catalog.set_config('search_path', '', false)");
238+
if (PQresultStatus(res) != PGRES_COMMAND_OK)
239+
{
240+
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
241+
PQclear(res);
242+
exit_nicely(conn);
243+
}
244+
PQclear(res);
245+
235246
res = PQexec(conn, "begin");
236247
PQclear(res);
237248
printf("importing file \"%s\" ...\n", in_filename);

‎src/test/examples/testlo64.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,17 @@ main(int argc, char **argv)
256256
exit_nicely(conn);
257257
}
258258

259+
/* Set always-secure search path, so malicous users can't take control. */
260+
res = PQexec(conn,
261+
"SELECT pg_catalog.set_config('search_path', '', false)");
262+
if (PQresultStatus(res) != PGRES_COMMAND_OK)
263+
{
264+
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
265+
PQclear(res);
266+
exit_nicely(conn);
267+
}
268+
PQclear(res);
269+
259270
res = PQexec(conn, "begin");
260271
PQclear(res);
261272
printf("importing file \"%s\" ...\n", in_filename);

0 commit comments

Comments
 (0)
Please sign in to comment.