Oracle中SQL注入高危点是EXECUTE IMMEDIATE和OPEN FOR拼接用户输入,唯一安全修复是正确使用绑定变量;绑定变量必须脱离SQL字符串上下文,且仅适用于值而非表名、列名等结构化部分。

Oracle 中只要用了 EXECUTE IMMEDIATE 或 OPEN FOR 拼接用户输入,就存在 SQL 注入风险;唯一可靠、官方强制要求的修复方式,是改用绑定变量——不是“建议”,而是必须。
为什么字符串拼接在 Oracle 里特别危险
Oracle 的 PL/SQL 引擎允许运行时构造完整语句,比如:EXECUTE IMMEDIATE 'SELECT * FROM users WHERE name = ''' || user_input || ''''。一旦 user_input 是 ' OR 1=1 --,整条语句就变成 SELECT * FROM users WHERE name = '' OR 1=1 --,条件恒真,数据库直接返回全表数据。
这种拼接绕过了预编译和类型校验:Oracle 只看到一条已拼好的字符串,不识别其中哪部分是代码、哪部分是数据,自然无法触发绑定变量的安全机制。
- 常见错误现象包括查出不该查的数据,或更隐蔽地——不报错、却越权访问
- 哪怕加了
REPLACE(user_input, '''', '''''')过滤单引号,也防不住 Unicode 编码绕过、宽字节截断、--和/* */组合等变体攻击 -
DBMS_ASSERT.SQL_OBJECT_NAME只能用于校验表名、列名等对象名,不能用于 WHERE 条件里的值
EXECUTE IMMEDIATE 中绑定变量怎么写才真正生效
关键不是“写了冒号”,而是变量是否脱离 SQL 字符串上下文。下面这三种写法中,只有最后一种安全:
❌ 错误:仍是字符串拼接EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = ' || dept_id;
❌ 错误:冒号被拼进字符串字面量里,Oracle 当作普通文本处理EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = :1' USING dept_id;(注意:这里引号内含空格或换行会导致语法错误,实际应无空格)
✅ 正确:SQL 字符串是静态字面量,:1 是独立占位符,由 Oracle 引擎统一处理转义与类型EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = :1' USING dept_id;
-
USING子句中的变量必须是已声明的 PL/SQL 变量(如VARCHAR2、NUMBER),不能是表达式(如USING UPPER(name)不合法,需先v_name := UPPER(name);再绑定) - 多个参数按位置顺序匹配:
:1对应第一个USING值,:2对应第二个;命名绑定(:dept_id)更推荐,避免顺序错位 - IN/OUT 参数需显式声明方向,例如
USING IN v_id, OUT v_name;输出要用INTO或RETURNING INTO,且目标变量必须已声明 - 占位符数量与
USING值数量必须严格一致,否则报ORA-01008: not all variables bound
什么时候不能用绑定变量?结构化部分必须白名单校验
绑定变量只适用于“值”(WHERE 条件、INSERT VALUES、UPDATE SET 的右值),**不支持表名、列名、ORDER BY 字段、GROUP BY 表达式等结构化内容**。
比如以下写法全部非法:
EXECUTE IMMEDIATE 'SELECT * FROM ' || table_name || ' WHERE id = :1' USING v_id;EXECUTE IMMEDIATE 'SELECT ' || col_list || ' FROM emp' USING v_id;
- 这类场景必须走白名单校验:把允许的表名/列名硬编码为常量集合,用
CASE或IF显式比对 - 或使用
DBMS_ASSERT包(仅限 Oracle 12c+):DBMS_ASSERT.SQL_OBJECT_NAME(table_name)可校验对象名合法性,但不能替代白名单逻辑 - 正则过滤(如
REGEXP_LIKE(table_name, '^[a-zA-Z][a-zA-Z0-9_]*$'))不可靠,易被绕过
性能影响与 DBMS_SQL 的取舍
绑定变量不只是安全手段,还显著提升性能:它让结构相同的语句共享执行计划,减少硬解析(Hard Parse),缓解共享池闩锁竞争。高并发下,这点比安全收益更直观。
-
EXECUTE IMMEDIATE+ 绑定变量适合 95% 场景:SQL 结构固定,仅值变化(如增删改查、带RETURNING) -
DBMS_SQL更底层灵活,但要手动开游标、定义列、绑定、执行、关游标;未调用DBMS_SQL.CLOSE_CURSOR会快速耗尽游标资源,引发ORA-01000 - 除非 WHERE 条件数完全动态(如搜索页多选字段组合),否则别为“看起来更灵活”选
DBMS_SQL
最容易被忽略的一点:所有外部输入——HTTP 参数、表单、文件读取、甚至其他表字段值——只要参与动态 SQL 构造,就必须走绑定变量或白名单;没有“这个参数看起来很安全”的例外。











