oracle中防sql注入唯一可靠方式是execute immediate配合using子句传参,严禁拼接字符串;占位符按出现顺序匹配参数,重复需重复传递;表名列名等结构信息须白名单校验或dbms_assert校验。

EXECUTE IMMEDIATE必须配USING,不能拼字符串
Oracle里防SQL注入唯一靠谱的路就是用USING子句传参,拼接用户输入等于把执行权交出去。哪怕一个单引号、一个空格、一个Unicode变体,都可能让EXECUTE IMMEDIATE执行任意语句。
错误写法:EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE name = ''' || user_name || '''';——如果user_name是' OR 1=1 --,整张表就暴露了。
正确写法:EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE name = :n' INTO v_name USING user_name;
-
:n是命名占位符,但Oracle按**出现顺序**匹配USING后的参数,不是靠名字查 -
USING后变量名(user_name)可以和占位符名(:n)不同,但类型、顺序、数量必须严格一致 - 多个参数时,逗号分隔:
USING val1, val2, val3,不是USING (val1, val2, val3)
IN子句怎么安全传多个值
WHERE id IN (:a, :b, :c)看着自然,但Oracle不支持运行时动态展开绑定列表。直接传数组或集合变量会报ORA-01008: not all variables bound。
安全做法只有两个:
- 固定最多N个占位符(如最多10个):
WHERE id IN (:1,:2,:3,:4,:5,:6,:7,:8,:9,:10),USING传10个变量,未用的填NULL,再加AND id IS NOT NULL过滤 - 改用集合类型+
TABLE()函数:WHERE id IN (SELECT COLUMN_VALUE FROM TABLE(:id_list)),USING传sys.odcinumberlist(1,2,3)
千万别用LISTAGG拼字符串再INSTR查,那等于绕开绑定、重回高危区。
重复占位符要重复传值,不能省
如果SQL里同一个占位符出现多次,比如'INSERT INTO t(a,b,c) VALUES (:x, :x, :y)',Oracle要求每个出现位置都对应一个USING参数。
必须写成:EXECUTE IMMEDIATE sql_stmt USING a, a, b;
常见错误:EXECUTE IMMEDIATE 'INSERT INTO t(a,b,c) VALUES (:x, :x, :y)' USING a, b;——报ORA-01008,因为只传了2个值,但SQL里有3个占位符。
注意:这是针对SQL语句的规则;如果是PL/SQL块(带BEGIN ... END;),重复占位符才按名字复用一个参数。
表名、列名等结构信息不能绑,但也不能乱拼
绑定变量只适用于值(WHERE条件、SET赋值等),不适用于对象名。但直接拼user_table或order_by_col一样危险。
正确姿势:
- 白名单校验 +
DBMS_ASSERT.SIMPLE_SQL_NAME(user_table),它会抛异常拦住非法字符 - 别信正则过滤,挡不住Unicode变体或绕过技巧
- 如果真要动态字段排序,用
CASE预置逻辑,再配合白名单判断
DBMS_ASSERT.SQL_OBJECT_NAME仅用于对象名校验,不能用于WHERE条件值。











