copy是postgresql高危入口,因默认可读写任意文件路径,易遭注入导致敏感信息泄露或恶意写入;须禁用pg_read_server_files/pg_write_server_files权限、禁用superuser,并改用客户端驱动流式导入。

COPY 是 PostgreSQL 提供的高效批量导入导出命令,但它默认允许读写任意文件系统路径(如 COPY ... FROM '/etc/passwd'),一旦被注入或误配,攻击者可直接读取敏感配置、密钥文件,甚至写入恶意脚本到 /tmp 或 Web 目录下。更危险的是,它常与动态拼接的 SQL 一起使用(比如在 PL/pgSQL 函数中拼接文件路径),极易触发双重漏洞:目录遍历 + SQL 注入。
PostgreSQL 从 9.5 起引入了细粒度角色权限,COPY 的文件访问能力不再绑定于 superuser,而是由两个独立权限控制:
-
pg_read_server_files:允许COPY … FROM读取服务器文件 -
pg_write_server_files:允许COPY … TO写入服务器文件
这两个角色默认只授予 postgres 用户。生产环境必须:
- 撤销应用连接账号对
pg_read_server_files和pg_write_server_files的成员资格:REVOKE pg_read_server_files FROM app_user; - 禁止应用账号拥有
SUPERUSER属性:ALTER ROLE app_user NOSUPERUSER; - 若业务真需批量导入,改用客户端驱动(如
psycopg2.copy_expert())在应用层完成数据传输,而非服务端COPY
这是最常见也最致命的错误写法。例如以下函数会直接导致目录遍历:
CREATE OR REPLACE FUNCTION load_csv(filename TEXT) RETURNS VOID AS $\( BEGIN EXECUTE 'COPY users FROM ''' || filename || ''' CSV HEADER'; END; \)$ LANGUAGE plpgsql;
调用 SELECT load_csv(‘/etc/passwd’); 就能读取任意文件。正确做法只有两种:
- 完全禁用该函数,改用预定义白名单路径 +
pg_read_server_files临时授权(极不推荐) - 彻底重构为应用层处理:前端上传文件 → 后端接收二进制流 → 使用
cursor.copy_expert()(Python)或client.copyFrom()(Node.js)将数据送入数据库,全程不暴露文件路径给数据库服务端 - 若必须用服务端
COPY,则路径必须硬编码,且仅限/var/lib/postgresql/data/import/这类专用隔离目录,并由 DBA 手动设置目录权限(如chown postgres:postgres /var/lib/postgresql/data/import/)
真正兼顾性能与安全的方式,是放弃服务端 COPY,转而依赖驱动层提供的安全导入接口:
- Python(psycopg2):
cursor.copy_expert("COPY users (name, email) FROM STDIN WITH (FORMAT CSV, HEADER TRUE)", file_obj)—— 文件内容由 Python 进程读取并传入,数据库无法访问磁盘路径 - Node.js(pg):
client.copyFrom(‘users (name, email)’, rows, { format: ‘csv’ })——rows是内存数组,无路径参与 - Java(pgjdbc):
PGCopyOutputStream或copyIn()方法,同样绕过服务端文件系统
这些接口天然隔离文件路径,且与参数化查询机制一致,不会因用户输入改变语义。唯一要注意的是:仍需对上传文件内容做格式校验(如限制 CSV 行数、字段长度、禁止 NULL 字节),防止 DoS 类攻击。
真正难防的不是语法错误,而是把 COPY 当成“快捷方式”后,顺手把路径变量扔进 EXECUTE —— 那等于主动拆掉数据库的最后一道门锁。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/260872.html