跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

怎样防范Oracle数据库中的SQL注入攻击_使用DBMS_ASSERT包校验输入

DBMS_ASSERT是Oracle内置PL/SQL包,用于校验输入字符串是否符合SQL标识符规范,仅适用于表名、列名等元数据场景,不适用于业务字段值,不能替代绑定变量。 Oracle数据库中用
DBMS_ASSERT
包校验输入,能有效阻断部分SQL注入路径,但它不是万能补丁——它只在「输入必须严格匹配预设模式」的场景下才真正起效,且无法替代参数化查询。 DBMS_ASSERT 是什么,适合在哪种场景用
DBMS_ASSERT
是 Oracle 自带的 PL/SQL 包,提供一系列函数用于对字符串输入做格式校验和转义。它不处理 SQL 逻辑拼接,也不改变执行计划,只做「输入是否符合预期形态」的断言。 适用于:存储过程中接收用户传入的表名、列名、schema 名等元数据(metadata)类字符串 不适用于:普通业务字段值(如用户名、邮箱、搜索关键词),这类内容必须走绑定变量 典型误用:用
DBMS_ASSERT.ENQUOTE_NAME
处理用户提交的密码或地址,这毫无防护意义,反而制造虚假安全感 常用函数及关键参数差异 不同函数行为差异极大,选错就等于没防:
DBMS_ASSERT.SIMPLE_SQL_NAME
:只允许字母、数字、下划线、$、#,长度 ≤ 30,且不能是 Oracle 保留字。适合校验列名或别名
DBMS_ASSERT.QUALIFIED_SQL_NAME
:支持点号分隔(如
SCHEMA.TABLE
),但每个段仍需满足
SIMPLE_SQL_NAME
规则
DBMS_ASSERT.ENQUOTE_NAME
:给字符串加双引号并转义内部双引号,但**不校验合法性**,仅用于构造动态 SQL 中的标识符,不能代替校验
DBMS_ASSERT.NOOP
:不做任何处理,仅返回原值——这是个陷阱函数,文档里写“用于调试”,实际生产中基本不该出现 例如,想校验传入的排序字段是否合法: oracle知识库 oracle知识库下载 下载
DECLARE 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;

为什么 DBMS_ASSERT 不能替代绑定变量 根本原因在于:它不参与 SQL 解析阶段的语义隔离,而只是在 PL/SQL 层做字符串检查。一旦校验通过,后续拼接仍属动态 SQL,存在逻辑绕过可能。 攻击者可利用合法标识符组合恶意逻辑,例如传入
'password' || ' = ''x'' --'
(虽然会被
SIMPLE_SQL_NAME
拦住,但若用错函数就失效)
DBMS_ASSERT
对数字型注入完全无效——比如
id=1 OR 1=1
,只要传的是数字类型参数,根本进不到这个包的校验逻辑里 所有基于
EXECUTE IMMEDIATE
或
DBMS_SQL
的动态执行,只要含未绑定的用户输入,风险等级就远高于静态 SQL + 绑定变量 容易踩的坑和真实报错表现 调用失败时抛出
ORA-44001: invalid schema
、
ORA-44002: invalid object name
等错误,不是异常,而是设计行为——它本意就是让非法输入直接中断执行。 常见翻车点:把用户输入的中文字段名、含空格的别名、带连字符的自定义标识符直接喂给
SIMPLE_SQL_NAME
,必然报
ORA-44002
另一个坑:在应用层已做过滤(如去除了单引号),又在存储过程中重复调用
DBMS_ASSERT
,看似多一层保险,实则掩盖了本该用绑定变量解决的问题 注意版本兼容性:
DBMS_ASSERT
在 Oracle 10gR2 引入,但早期版本中部分函数行为有差异,比如
QUALIFIED_SQL_NAME
在 11g 之前不校验 schema 是否真实存在 真正关键的防线始终是:对所有非元数据类用户输入,一律使用
:bind_variable
;只有当业务确实需要动态指定表名/列名时,才谨慎引入
DBMS_ASSERT
,并配以最小权限账号与白名单式 Schema 控制。

相关文章