oracle pl/sql中execute immediate或open for拼接用户输入即存在sql注入风险;唯一可靠解法是彻底禁用字符串拼接,对值一律使用using绑定变量,对表名、列名等结构化部分必须通过白名单或dbms_assert校验。

Oracle PL/SQL 中只要用了 EXECUTE IMMEDIATE 或 OPEN FOR 拼接用户输入,就存在 SQL 注入风险;唯一可靠解法是彻底剥离字符串拼接,改用绑定变量(USING 子句),且仅对值生效——表名、列名、排序字段等结构化部分不能绑定,必须走白名单校验。
为什么 EXECUTE IMMEDIATE 拼接字符串等于开后门
Oracle 不会对已拼好的字符串做语法重解析或参数识别。比如这行代码:
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE name = ''' || user_name || '''';
一旦 user_name 是 ' OR 1=1 --,最终执行的就是一条完整、合法、但语义被篡改的 SQL。数据库只看到字符串,不区分哪部分是逻辑、哪部分是数据。
常见错误现象包括:看似加了单引号过滤、用了 REPLACE(user_name, '''', ''''''),但攻击者可用 Unicode 变体、宽字节截断、注释符组合绕过;DBMS_ASSERT.SQL_OBJECT_NAME 这类函数只能校验对象名,对 WHERE 条件里的值完全无效。
USING 子句怎么写才算真正生效
关键不是“有没有冒号”,而是变量是否脱离 SQL 字符串上下文。下面三种写法中,只有最后一种安全:
- ❌
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = ' || dept_id;—— 纯拼接,高危 - ❌
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = :1' USING dept_id;—— 冒号在字符串里,仍是字面量,未触发绑定 - ✅
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = :1' USING dept_id;—— 冒号独立于字符串,由 Oracle 引擎接管类型与转义
注意:USING 后只能跟 PL/SQL 变量(如 VARCHAR2、NUMBER),不能是表达式:USING UPPER(name) 非法;必须先 l_name := UPPER(name);,再 USING l_name。
结构化部分(表名、列名)没法绑定,怎么办
绑定变量只适用于值,不适用于对象名、关键字、ORDER BY 字段等动态结构。这类场景没有“安全拼接”一说,必须强制白名单控制:
- 用
DBMS_ASSERT.SQL_OBJECT_NAME校验传入的表名或列名是否为合法对象名(仅限 Oracle 12c+) - 把允许的排序字段写死在 CASE 或 IF 判断里,例如:
IF p_order_by = 'NAME' THEN v_order := 'ORDER BY name'; ELSIF p_order_by = 'ID' THEN v_order := 'ORDER BY id'; END IF; - 禁止任何外部输入直接进入动态 SQL 的非值位置;哪怕加了双引号或转义,也挡不住绕过手段
最常被忽略的一点:开发时容易认为“我只拼接了数字型参数,所以没问题”,但只要拼接逻辑存在,就等于埋下可利用路径——Oracle 不会替你判断输入来源是否可信,它只认语法结构。











