当前位置: 首页
数据库
PostgreSQL安全PL/pgSQL函数防注入编写指南

PostgreSQL安全PL/pgSQL函数防注入编写指南

时间:2026-06-23
转载

在PL pgSQL中编写动态SQL时,仅依赖quote_ident()无法防注入,需先通过正则白名单校验标识符;参数必须用USING绑定;禁止在函数内拼接COPY命令路径,应硬编码;避免依赖format()占位符,需逐条分支进行安全检查。

在PL/pgSQL里写动态SQL,很多开发者最先想到的防护就是 quote_ident()。但如果你觉得只要套上这个函数就万事大吉,那可真是踩进了一个大坑。

先说结论:动态拼接表名或字段名时,quote_ident() 只能算是最低限度的防御,绝对不够用。它只负责把你传进来的字符串用双引号包起来,再把里面的反斜杠转义一下。但问题是,它从来不问“你这字符串到底是不是一个合法的标识符”。来看一个典型例子:你传进来一个 'users; DROP TABLE accounts; --'quote_ident() 会把它转换成 "users; DROP TABLE accounts; --"。你看,双引号是包上了,可这句话放到 EXECUTE 'SELECT * FROM ' || quote_ident(user_input) 里,它仍然是语法上合法的标识符——数据库不会报错,也不会拦截后面的恶意操作。等于说,你给了攻击者一本正经构造恶意字符串的通行证。

那真正安全的做法是什么?很简单:先白名单校验,再调 quote_ident()。举个例子,你可以只允许输入由字母、数字、下划线组成,长度不超过64个字符,并且不能以数字开头:

IF user_input !~ '^[a-zA-Z_][a-zA-Z0-9_]{0,63}$' THEN
  RAISE EXCEPTION 'invalid identifier: %', user_input;
END IF;
  • 正则白名单必须放在 EXECUTE 之前执行,不能指望 quote_ident() 来兜底。
  • 处理表名或字段名时,千万不要用 quote_literal()——它给你加的是单引号,一执行就语法报错。
  • 也别直接把用户输入扔进 format()%I 占位符里,除非你已经做了白名单过滤。

动态查询中传参必须用 USING,不能拼进 SQL 字符串

这条坑埋得特别深,也特别容易被忽略。你可能会觉得自己已经用 quote_ident() 安全地处理了表名,WHERE 条件里的值随便拼一下也没事——但就是这“随便拼一下”,可能直接把注入入口暴露给攻击者。

-- 危险!拼接值 = 注入入口
EXECUTE 'SELECT * FROM ' || quote_ident(tbl) || ' WHERE id = ' || user_id;

业内公认的正确写法,是把查询值作为参数传给 EXECUTE ... USING

EXECUTE 'SELECT * FROM ' || quote_ident(tbl) || ' WHERE id = $1' USING user_id;
  • USING 后面的变量,数据库引擎会当作纯粹的数据绑定,完全脱离 SQL 解析上下文——这等于从根上断了注入的路。
  • 支持多个参数,比如 USING val1, val2, val3,对应 SQL 里的 $1, $2, $3
  • 有一点必须牢记:USING 里不能传表名、字段名、ORDER BY 子句——这些还是得走白名单 + quote_ident() 的组合拳。

COPY 命令在函数里绝对禁止拼接路径

这大概是最容易被忽视的高危操作了。在 PL/pgSQL 函数里写 EXECUTE 'COPY users FROM ''' || filename || '''',等于直接给攻击者开了一扇任意文件读取的大门。比如攻击者可以调用 load_csv('/etc/passwd'),直接把系统密码文件泄露出去。

生产环境下,唯一靠谱的方案是:彻底放弃在服务端用 COPY,改为由应用层流式导入(比如 Python 的 cursor.copy_expert())。如果实在绕不开,必须满足以下三个条件:

  • 路径必须硬编码,比如 '/var/lib/postgresql/import/users.csv',绝对不要允许任何变量参与路径拼接。
  • 数据库角色必须撤销 pg_read_server_filespg_write_server_files 权限。
  • 函数本身用 SECURITY DEFINER 并限制为只读角色,只对特定目录有访问权限。

为什么不用 format() 的 %L 或 %I 就容易出事

format() 用起来确实方便,但它的占位符行为完全依赖你传入的参数类型和上下文。比如 %L 对空字符串、NULL、特殊字符的处理,远不如 quote_literal() 稳定;%I 虽然等价于 quote_ident(),但它同样不会主动拒绝非法输入——它只是尽力转义,而不是校验。

更麻烦的是,在嵌套调用或复杂表达式里,format() 很容易漏掉某个占位符,导致未格式化的原始输入直接混进最终 SQL。所以建议这样处理:

  • 优先拆解逻辑:白名单校验 → quote_ident() / quote_literal()EXECUTE ... USING,步步到位。
  • 避免在同一个 format() 调用里同时处理标识符和值。
  • 测试时一定要覆盖边界输入:空字符串、单引号、分号、反斜杠、Unicode 控制字符——一个都不能少。

说到底,写对一行 EXECUTE 并不难,难的是确保函数里的每一条分支路径——IF 分支、异常处理块、循环体里的每一次拼接——都经过相同的安全检查。这才是真正的安全底线。

在PostgreSQL中如何编写安全的PL/pgSQL函数防止注入?

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
Redis是什么:核心特性、架构与应用场景解析

Redis是什么:核心特性、架构与应用场景解析

Redis是一款基于内存的键值型NoSQL数据库,以超高读写速度和丰富的数据结构著称。本文系统梳理Redis的核心特性、架构组成、性能优势及典型应用场景,并通过与Memcached、MySQL、MongoDB的对比,帮助开发者快速判断Redis是否适合当前业务需求。

时间:2026-09-01 06:20
Windows 安装 MongoDB 完整图文教程

Windows 安装 MongoDB 完整图文教程

本文详细介绍在 Windows 系统上安装 MongoDB 的完整流程。从官网下载 MSI 安装包开始,逐步演示自定义安装路径、配置 Windows 服务、跳过 MongoDB Compass 等关键选项,并提供通过系统服务列表验证安装是否成功的方法,帮助开发者快速搭建本地 MongoDB 环境。

时间:2026-09-01 06:20
Linux 安装 MongoDB 完整指南:依赖配置、环境变量与服务启动

Linux 安装 MongoDB 完整指南:依赖配置、环境变量与服务启动

本文详解在 Linux 系统下安装 MongoDB 的完整流程,涵盖依赖包安装、二进制包下载解压、环境变量配置、数据与日志目录创建及服务启动验证。通过标准化命令与路径说明,帮助开发者快速完成部署并确认服务状态。

时间:2026-09-01 06:20
MacOS安装MongoDB完整教程

MacOS安装MongoDB完整教程

本文介绍在MacOS系统下安装MongoDB的完整流程,涵盖下载、解压、目录配置、环境变量设置及服务启动。通过明确的命令与参数说明,帮助开发者快速完成环境搭建并验证安装结果。

时间:2026-09-01 06:19
Ubuntu系统安装与配置Redis完整指南

Ubuntu系统安装与配置Redis完整指南

本文详解在Ubuntu系统中安装Redis的两种主流方式:apt在线安装与源码编译安装。涵盖版本选择逻辑、服务启停与状态检查、连接验证方法,以及在线练习工具与桌面GUI客户端的对比与使用建议,帮助开发者快速搭建并验证Redis运行环境。

时间:2026-09-01 06:19
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全