April 4, 2019·4 min read·engineeringpostgresqlsecurityprivileges

PostgreSQL explains the privilege boundary behind COPY PROGRAM

The PostgreSQL Security Team disputes CVE-2019-9193 because server-side program execution is an explicitly privileged capability. The distinction affects how database roles should be assigned.

A database account that can run programs as the database server's operating-system user has authority beyond SQL tables. PostgreSQL deliberately provides that capability to trusted administrators. The dispute over CVE-2019-9193 concerns whether exercising that granted authority constitutes a vulnerability in the database.

In its April 4 statement, the PostgreSQL Security Team says the CVE was filed in error and that it has contacted the reporter. The statement identifies the permission required for COPY ... PROGRAM and explains that PostgreSQL does not define a security boundary between a database superuser and the operating-system account running the server.

I prefer PostgreSQL's explicit account of this boundary. A security model is useful when it tells an administrator what a grant actually permits. Treating a database superuser as an account confined to ordinary table access would create a false expectation about the system being administered.

Where the command executes

The PostgreSQL 11 COPY documentation distinguishes server-side operations from data sent through the client connection. A filename in SQL COPY refers to a file accessible to the server process. Likewise, PROGRAM names a command that the server executes.

For COPY FROM PROGRAM, PostgreSQL reads the command's standard output. For COPY TO PROGRAM, it sends the copied data to the command's standard input. In both cases, the command runs with the authority of the operating-system user running PostgreSQL. Its location and permissions follow the server, even if a client submits the SQL over a remote connection.

The documentation uses compression as an example of why this exists. Exported table data can pass through a server-side compression program before reaching a file. That operation necessarily involves more authority than selecting rows from the table, because it creates a process and accesses server resources.

The command also runs through a shell. The manual recommends a fixed command string and warns against passing untrusted input into it. A program-execution grant therefore authorizes a facility whose argument handling has its own security implications. It does not turn arbitrary application input into a safe operating-system command.

This explains why ordinary data access and server-side program execution require separate consideration. A user can have legitimate permission to export table rows without needing permission to start a process on the database host. The mechanism chosen for the export determines which additional authority is necessary.

Which role grants execution

The Security Team identifies two routes to COPY ... PROGRAM. A database superuser can use it, and a user granted pg_execute_server_program can use it. The statement explicitly says that pg_read_server_files and pg_write_server_files, mentioned in the CVE, do not grant permission to execute this command.

These distinctions matter because the roles have different immediate capabilities. The default-role documentation describes server-file reading, server-file writing and server-program execution separately. The execution role permits programs to run as the database server's operating-system user.

The same documentation warns that these server-access roles are powerful. Direct file access bypasses database-level permission checks, and the roles could be used to obtain superuser-level access. The fact that one role does not directly authorize COPY PROGRAM is therefore not a reason to treat unrestricted server-file access as harmless.

The correct reading preserves both points. The command has an explicit execution privilege check. Other server-access grants can still carry consequences far beyond an ordinary SQL permission. Accurate security analysis needs to name the grant involved and the operation it authorizes, without collapsing every privileged role into one interchangeable category.

For application accounts, that makes the choice of grants a design decision. A requirement to read application tables does not imply a requirement to read arbitrary server files. A requirement to load data does not automatically justify server-program execution. Those capabilities should have an identified administrative purpose and a trusted recipient.

Client-side copying has a different location

The manual distinguishes SQL COPY from the psql command \copy. The latter uses COPY FROM STDIN or COPY TO STDOUT, with the client reading or writing the local file. File accessibility follows the account running the client rather than the account running the database server.

That difference offers a useful way to reason about an import or export request. If a user needs a local CSV file, the file can belong to the client-side workflow. The task does not inherently require opening a path on the database host. The table permissions still matter, but the operation need not gain server filesystem authority merely because it transfers many rows.

This is a distinction about execution location, not a blanket statement that one copy form is always preferable. Administrative workflows can legitimately require server-side files or programs. The database documents those forms because they solve real tasks, while reserving their additional authority for trusted roles.

My own preference is to make that authority visible in the account model. A narrowly described application account is easier to understand when it does not also carry server-administration privileges. The objection to this CVE does not weaken that preference. It clarifies why those grants deserve scrutiny in the first place.

Superuser authority already crosses into the host account

The Security Team says that adding COPY PROGRAM in PostgreSQL 9.3 did not create a new security boundary. It added another command within the boundaries that already existed. Database superusers already occupy a level of trust that includes actions as the server's operating-system user.

The statement also explains why PostgreSQL must not run as an operating-system superuser such as root. The database's own administrative authority should not imply unrestricted authority over the host. Running the server under its designated account limits the operating-system identity available to database-level administrative actions.

As of this statement, the project's response is to dispute the vulnerability classification, not to announce a patch that removes the intended feature. Its practical guidance is to avoid granting superuser access to remote or otherwise untrusted users. The same reasoning applies when granting pg_execute_server_program, whose documented purpose is to authorize server-side execution under the database service account.