oracle pl/sql中动态sql必须用绑定变量替代字符串拼接,表名等对象名需白名单校验,禁用dbms_sql.v7,dbms_assert不能替代安全设计。
oracle pl/sql中只要用了 execute immediate 或 dbms_sql.parse 拼接用户输入,就极大概率存在高危sql注入漏洞——这不是配置问题,是代码逻辑缺陷,必须重构。
用绑定变量替代字符串拼接
所有动态SQL骨架必须固定,仅允许数据通过绑定变量传入。拼接表名、列名、WHERE条件片段属于硬编码式错误,EXECUTE IMMEDIATE 和 DBMS_SQL.PARSE 都不接受这种写法。
- 错误示例:
v_sql := 'SELECT * FROM ' || p_table_name || ' WHERE id = ' || p_id; - 正确做法:骨架中只保留确定的对象名和结构,如
'SELECT * FROM employees WHERE id = :id',再用USING或DBMS_SQL.BIND_VARIABLE传值 -
USING语法更简洁,适用于简单场景;DBMS_SQL更灵活但需手动管理游标、类型声明和错误清理 - 绑定变量名必须带冒号前缀(如
:id),否则 Oracle 不识别为占位符
对象名动态化必须走 DBMS_ASSERT(且有前提)
如果业务真需要运行时决定表名或 schema 名(比如多租户分表),不能靠拼接,也不能靠 DBMS_ASSERT.SQL_OBJECT_NAME 盲目兜底。
-
DBMS_ASSERT.SQL_OBJECT_NAME只校验是否为合法对象名,不防逻辑注入(如输入"employees"; DROP TABLE logs --仍会通过) -
DBMS_ASSERT.SIMPLE_SQL_NAME仅允许字母数字下划线,不支持连字符、双引号标识符或 schema. 表名格式 - 真正安全的做法是:用白名单控制可选对象范围,比如把允许的表名预存在
sys_context或配置表中,查表校验后再拼接 - 若必须用
DBMS_ASSERT,调用前要确认输入已从 HTTP 参数、UTL_HTTP 响应等不可信通道剥离
DBMS_SQL.PARSE 的 language_flag 别设成 V7
设置 language_flag => DBMS_SQL.V7 会绕过部分语法校验,让非法构造的语句更容易通过解析阶段,增大注入成功概率。
- 始终使用
DBMS_SQL.NATIVE(默认值),它启用完整语法检查和权限验证 - 老系统迁移时容易遗漏这个参数,默认值可能被覆盖,建议全局 grep 检查所有
DBMS_SQL.PARSE调用 - 配合绑定变量使用时,
V7还可能导致绑定类型推断异常,引发运行时报错
别把 DBMS_ASSERT 当万能过滤器
DBMS_ASSERT 是校验工具,不是消毒液。它无法修复设计缺陷,反而可能掩盖更深层的问题。
-
DBMS_ASSERT.ENQUOTE_NAME只加双引号,不转义内容——输入"employees"; TRUNCATE TABLE audit_log会被原样执行 - 对 WHERE 条件类输入(如
p_where_clause),必须走绑定变量,DBMS_ASSERT完全不适用 - 一旦发现代码里大量依赖
DBMS_ASSERT来“兜底”拼接逻辑,说明架构已偏离安全基线,应优先重构为静态骨架 + 绑定变量
最易被忽略的是:哪怕用了绑定变量,如果游标未关闭、异常未捕获、资源未释放,长期运行后仍可能触发 ORA-01000(打开游标过多)这类稳定性问题——安全不只是防注入,更是让防御逻辑本身健壮可落地。











