DBMS_ASSERT是Oracle内置PL/SQL包,用于校验输入字符串是否符合SQL标识符规范,仅适用于表名、列名等元数据场景,不适用于业务字段值,不能替代绑定变量。
Oracle数据库中用
包校验输入,能有效阻断部分SQL注入路径,但它不是万能补丁——它只在「输入必须严格匹配预设模式」的场景下才真正起效,且无法替代参数化查询。
DBMS_ASSERT 是什么,适合在哪种场景用
是 Oracle 自带的 PL/SQL 包,提供一系列函数用于对字符串输入做格式校验和转义。它不处理 SQL 逻辑拼接,也不改变执行计划,只做「输入是否符合预期形态」的断言。
适用于:存储过程中接收用户传入的表名、列名、schema 名等元数据(metadata)类字符串
不适用于:普通业务字段值(如用户名、邮箱、搜索关键词),这类内容必须走绑定变量
典型误用:用
处理用户提交的密码或地址,这毫无防护意义,反而制造虚假安全感
常用函数及关键参数差异
不同函数行为差异极大,选错就等于没防:
:只允许字母、数字、下划线、$、#,长度 ≤ 30,且不能是 Oracle 保留字。适合校验列名或别名
:支持点号分隔(如
),但每个段仍需满足
规则
:给字符串加双引号并转义内部双引号,但**不校验合法性**,仅用于构造动态 SQL 中的标识符,不能代替校验
:不做任何处理,仅返回原值——这是个陷阱函数,文档里写“用于调试”,实际生产中基本不该出现
例如,想校验传入的排序字段是否合法:
oracle知识库
oracle知识库下载
下载
为什么 DBMS_ASSERT 不能替代绑定变量
根本原因在于:它不参与 SQL 解析阶段的语义隔离,而只是在 PL/SQL 层做字符串检查。一旦校验通过,后续拼接仍属动态 SQL,存在逻辑绕过可能。
攻击者可利用合法标识符组合恶意逻辑,例如传入
(虽然会被
拦住,但若用错函数就失效)
对数字型注入完全无效——比如
,只要传的是数字类型参数,根本进不到这个包的校验逻辑里
所有基于
或
的动态执行,只要含未绑定的用户输入,风险等级就远高于静态 SQL + 绑定变量
容易踩的坑和真实报错表现
调用失败时抛出
、
等错误,不是异常,而是设计行为——它本意就是让非法输入直接中断执行。
常见翻车点:把用户输入的中文字段名、含空格的别名、带连字符的自定义标识符直接喂给
,必然报
另一个坑:在应用层已做过滤(如去除了单引号),又在存储过程中重复调用
,看似多一层保险,实则掩盖了本该用绑定变量解决的问题
注意版本兼容性:
在 Oracle 10gR2 引入,但早期版本中部分函数行为有差异,比如
在 11g 之前不校验 schema 是否真实存在
真正关键的防线始终是:对所有非元数据类用户输入,一律使用
;只有当业务确实需要动态指定表名/列名时,才谨慎引入
,并配以最小权限账号与白名单式 Schema 控制。
DBMS_ASSERTDBMS_ASSERTDBMS_ASSERT.ENQUOTE_NAMEDBMS_ASSERT.SIMPLE_SQL_NAMEDBMS_ASSERT.QUALIFIED_SQL_NAMESCHEMA.TABLESIMPLE_SQL_NAMEDBMS_ASSERT.ENQUOTE_NAMEDBMS_ASSERT.NOOPDECLARE
p_order_by VARCHAR2(100) := 'name ASC';
safe_col VARCHAR2(100);
BEGIN
-- ❌ 错误:SIMPLE_SQL_NAME 不接受空格或 ASC/DESC
-- safe_col := DBMS_ASSERT.SIMPLE_SQL_NAME(p_order_by);
-- ✅ 正确:先拆出字段名,再单独校验
safe_col := DBMS_ASSERT.SIMPLE_SQL_NAME('name');
EXECUTE IMMEDIATE 'SELECT * FROM users ORDER BY ' || safe_col || ' ASC';
END;
'password' || ' = ''x'' --'SIMPLE_SQL_NAMEDBMS_ASSERTid=1 OR 1=1EXECUTE IMMEDIATEDBMS_SQLORA-44001: invalid schemaORA-44002: invalid object nameSIMPLE_SQL_NAMEORA-44002DBMS_ASSERTDBMS_ASSERTQUALIFIED_SQL_NAME:bind_variableDBMS_ASSERT