[PATCH] Add enable_copy_program GUC to control COPY PROGRAM
Ignat Remizov <ignat980@gmail.com>
From: Ignat Remizov <ignat980@gmail.com>
To: pgsql-hackers@lists.postgresql.org
Date: 2025-12-03T10:37:30Z
Lists: pgsql-hackers
Hi Postgres hackers,
Attached is a patch that introduces a new server-level configuration
parameter, "enable_copy_program", that allows administrators to disable
COPY ... PROGRAM functionality at the PostgreSQL server level.
MOTIVATION
==========
COPY ... PROGRAM is a powerful feature that allows executing arbitrary
shell commands from within PostgreSQL. While access is controlled via
the pg_execute_server_program role, some deployments may want to
completely disable this capability as a defense-in-depth measure.
This GUC provides that option.
In practice, COPY ... PROGRAM is a common code execution vector in
PostgreSQL-based attacks. It is:
- Simple to use (single SQL statement)
- Requires no additional extensions or setup
- Frequently targeted by automated botnets and malware campaigns
- Often the first technique attempted by attackers who gain superuser
access
While this GUC is not a comprehensive security solution, it serves as a
mitigating control that removes some of the lowest-hanging fruit for
attackers.
IMPORTANT SECURITY CONTEXT
==========================
This is a mitigating control, not a security boundary.
There is ongoing ecosystem friction around the disputed CVE-2019-9193 entry
in the NVD. The PostgreSQL Security Team has stated that this CVE does not
represent a security bug in PostgreSQL and was filed in error, but NVD and
other CVE databases still list it as a remote code execution issue via COPY
TO/FROM PROGRAM, and several commercial scanners and IDS/IPS signatures
treat
it as a high-severity vulnerability. This patch is not intended as a fix
for
that CVE; it simply provides an explicit configuration knob for
administrators
whose security tooling or policies require disabling program execution via
COPY.
Superusers retain multiple other avenues for executing operating system
commands, including but not limited to:
- Untrusted procedural languages (CREATE EXTENSION plpythonu, plperlu,
etc.)
- Custom extensions that provide shell access
- The LOAD command to load arbitrary shared libraries
- pg_read_file() / pg_write_file() combined with other techniques
- Foreign data wrappers with program execution capabilities
- Background workers in custom extensions
Disabling COPY ... PROGRAM does NOT make PostgreSQL secure against a
malicious superuser. However, it does:
1. Block a very common and highly automated attack vector – many botnet
payloads and exploit scripts specifically target COPY ... PROGRAM
because it requires no prerequisites.
2. Raise the bar for exploitation – attackers must use more complex,
less portable, or more detectable methods.
3. Reduce drive-by attacks – automated scanners and opportunistic
attackers often give up when their standard payload fails.
4. Help meet compliance requirements – some security frameworks mandate
disabling specific high-risk features.
5. Provide defense in depth – one layer in a broader security strategy.
KEY CHANGES
===========
New GUC parameter:
- Name: enable_copy_program
- Type: boolean
- Default: on (preserves existing behavior)
- Context: PGC_POSTMASTER (requires server restart to change)
- Flags: GUC_SUPERUSER_ONLY | GUC_DISALLOW_IN_AUTO_FILE
Implementation:
- copy.c
* Add a check in DoCopy() to reject COPY PROGRAM when disabled.
- guc_parameters.dat
* Register the new GUC parameter.
- guc.h
* Declare the enable_copy_program variable.
Documentation:
- config.sgml
* Document the parameter in the authentication section, including its
scope and limitations.
- copy.sgml
* Cross-reference the new parameter in the COPY command documentation.
- postgresql.conf.sample
* Add a commented sample entry.
Tests:
- copy.sql (regression tests) verifying:
* Default value is "on".
* ALTER SYSTEM SET is rejected (due to GUC_DISALLOW_IN_AUTO_FILE).
* SET is rejected (due to PGC_POSTMASTER context).
- t/050_copy_program.pl (TAP test) verifying:
* COPY TO PROGRAM works when enabled.
* COPY FROM PROGRAM works when enabled.
* Both are rejected with the expected error when disabled.
- Updated expected output files accordingly.
Misc:
- Fixed a typo in a copy.c comment:
* "pstdout | pstdout" -> "pstdin | pstdout".
BEHAVIOR
========
enable_copy_program COPY PROGRAM behavior
------------------- ------------------------------------------
on (default) Allowed (subject to role privileges)
off Rejected with error, even for superusers
Error message when disabled:
ERROR: COPY PROGRAM is disabled
HINT: Set enable_copy_program = on to allow COPY TO/FROM PROGRAM.
TESTING
=======
- All regression tests pass.
- Recovery TAP tests pass (injection-point skips are expected).
- New TAP test t/050_copy_program.pl validates both enabled and
disabled scenarios.
BACKWARD COMPATIBILITY
======================
This change is fully backward compatible. The default value "on"
preserves existing behavior. No action is required for existing
deployments unless they wish to disable COPY PROGRAM.
--
Ignat Remizov