dbms_assert不能防止sql注入,仅辅助校验元数据输入合法性;simple_sql_name校验简单标识符,qualified_sql_name校验schema.table格式,enquote_name和noop无防护作用,业务值必须用绑定变量。

DBMS_ASSERT 不能防止 SQL 注入,它只能辅助校验元数据类输入是否合法;用错函数、放错位置或混用于业务值,反而会制造虚假安全感。
DBMS_ASSERT.SIMPLE_SQL_NAME 只适合校验列名、别名
这个函数检查字符串是否为合法的简单 SQL 标识符:只含字母、数字、_、$、#,长度 ≤ 30,且不是 Oracle 保留字。它不接受空格、点号、ASC/DESC 等任何修饰。
- ✅ 正确用法:
safe_col := DBMS_ASSERT.SIMPLE_SQL_NAME('user_name'); - ❌ 错误用法:
DBMS_ASSERT.SIMPLE_SQL_NAME('user_name ASC')—— 空格和排序方向会直接触发ORA-44003: invalid SQL name - ⚠️ 注意:它返回原值,不转义、不加引号;拼接前仍需手动加双引号(如需)或确保上下文安全
DBMS_ASSERT.QUALIFIED_SQL_NAME 用于 schema.table 形式校验
适用于需要动态指定 schema 和表名的场景,但每个段落仍要满足 SIMPLE_SQL_NAME 规则,且中间只能用单个点号分隔。
- ✅ 正确:
DBMS_ASSERT.QUALIFIED_SQL_NAME('hr.employees') - ❌ 错误:
DBMS_ASSERT.QUALIFIED_SQL_NAME('hr."emp loyees"')—— 含空格和双引号,非法 - ❌ 错误:
DBMS_ASSERT.QUALIFIED_SQL_NAME('hr.employees; DROP TABLE logs --')—— 分号和注释符虽被拦在语法层,但若输入绕过校验(如通过 Unicode 零宽字符),QUALIFIED_SQL_NAME不做内容语义判断
千万别用 DBMS_ASSERT.ENQUOTE_NAME 或 NOOP 做防护
ENQUOTE_NAME 只是给字符串加双引号并转义内部双引号,**完全不校验合法性**;NOOP 则是“什么都不做”,纯属调试陷阱。
- ❌ 危险写法:
EXECUTE IMMEDIATE 'SELECT * FROM ' || DBMS_ASSERT.ENQUOTE_NAME(p_table)—— 输入"employees"; TRUNCATE TABLE audit_log会被原样拼进语句并执行 - ❌ 生产中不该出现:
DBMS_ASSERT.NOOP(p_input)—— 它等于没校验,还让人误以为加了防护 - ⚠️ 关键区别:
ENQUOTE_NAME返回带双引号的字符串,而SIMPLE_SQL_NAME返回无引号的干净标识符,后者更适合后续拼接控制
DBMS_ASSERT 对 WHERE 条件值完全无效
用户提交的搜索关键词、密码、邮箱、ID 等业务字段值,**一律不能走 DBMS_ASSERT**。它不处理单引号、Unicode 绕过、宽字节截断,也无法替代类型隔离。
- ❌ 典型误用:
DBMS_ASSERT.SIMPLE_SQL_NAME(p_password)—— 密码含特殊字符必然报错,且即使通过也毫无防护意义 - ✅ 唯一正确路径:这些值必须进绑定变量,例如
EXECUTE IMMEDIATE 'SELECT * FROM users WHERE email = :e' USING p_email; - ⚠️ 容易被忽略的点:哪怕你对
p_email做了正则过滤或REPLACE(p_email, '''', ''''''),Oracle 仍会在运行时把拼出的字符串当完整 SQL 解析——攻击者用U+FF07(全角单引号)就能绕过











