唯一安全方案是使用绑定变量(using),而非字符串拼接;拼接用户输入必然导致sql注入,dbms_assert和单引号过滤均无效。

EXECUTE IMMEDIATE 拼接字符串就是 SQL 注入入口
只要你在 EXECUTE IMMEDIATE 或 OPEN FOR 里用 || 拼接用户输入,不管加没加单引号过滤、REPLACE 或 DBMS_ASSERT,都算开着门等攻击者进来。Oracle 不会校验拼出来的字符串里有没有 ' OR 1=1 --,它只管语法合法就执行。
- 错误典型:
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE name = ''' || user_name || ''''—— 即使user_name是' OR 1=1 --,最终语句也变成SELECT * FROM emp WHERE name = '' OR 1=1 --',全表泄露 -
DBMS_ASSERT.SQL_OBJECT_NAME只能校验表名、列名,不能用于 WHERE 条件里的值;对值做REPLACE(user_input, '''', '''''')也防不住 Unicode 编码绕过或宽字节截断 - 真正安全的起点只有一个:让变量彻底脱离 SQL 字符串上下文
绑定变量必须写成静态字符串 + USING 才生效
冒号写进字符串里不等于用了绑定变量——关键看 Oracle 解析时能不能把占位符和 SQL 结构分开识别。
- ❌ 错误:
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = ' || dept_id—— 纯拼接,无防护 - ❌ 错误:
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = :1' USING dept_id—— 注意引号内若有空格或换行,PL/SQL 直接报ORA-06550语法错误;更隐蔽的是,如果:1被当成字符串字面量的一部分(比如前后有空格),Oracle 就当它是普通文本,不识别为占位符 - ✅ 正确:
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = :1' USING dept_id—— SQL 是完整静态字符串,:1是独立语法单元,由 Oracle 引擎在运行时统一转义、类型校验、代入 - 多个参数必须严格按位置匹配:
:1对应USING第一个变量,:2对应第二个;命名绑定(如:dept_id)更稳妥,避免顺序错位
IN 子句传多个值不能靠字符串拼,得用集合或预设占位符
WHERE id IN (:a, :b, :c) 看似合理,但 Oracle 不允许在 USING 里传“列表”——你传进去的永远是一个标量,不是一组值。
- ❌ 危险写法:
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE id IN (' || id_list_str || ')'—— 和拼表名一样致命 - ✅ 方案一(固定上限):SQL 中预写满占位符,如
WHERE id IN (:1,:2,:3,:4,:5),USING传 5 个变量,空余用NULL占位,业务逻辑需容忍无效值 - ✅ 方案二(推荐):用集合类型 +
TABLE()函数,例如WHERE id IN (SELECT COLUMN_VALUE FROM TABLE(:id_list)),USING传一个sys.odcinumberlist类型变量 -
USING后不能直接写表达式,如USING UPPER(name)非法;必须先赋值给已声明变量:v_name := UPPER(name); EXECUTE IMMEDIATE ... USING v_name;
绑定变量防注入 ≠ 自动高性能,类型不匹配照样拖垮查询
用了 USING 只锁死了注入路径,但若变量类型和字段类型不一致,Oracle 会触发隐式转换,导致索引失效、全表扫描。
- 字段是
VARCHAR2(10),你绑定NUMBER类型变量 → Oracle 把字段转成数字比对,索引失效 - 日期字段写
TO_DATE(:dt, 'yyyymmdd'),而:dt是字符串 → 函数索引无法命中,执行计划变差 - 建议:绑定变量声明类型尽量与目标字段一致;复杂格式转换(如大小写、日期解析)提到应用层或 PL/SQL 前置处理,别塞进动态 SQL 字符串里
- 特别注意:绑定变量会让优化器忽略具体值,对数据倾斜列(如状态码只有 'A'/'B'/'C',但 'A' 占 95%)可能生成次优执行计划;必要时可结合
/*+ BIND_AWARE */提示或分情况硬编码
真正难的不是写对 :1,而是确保整个动态 SQL 的每个可变部分——值、结构、上下文——都被纳入 Oracle 的安全边界内。表名、列名、ORDER BY 字段这些结构性内容,永远不能靠绑定变量,得用白名单校验或元数据查表确认。











