execute immediate 拼接字符串是sql注入高危入口,必须用绑定变量(using)替代值拼接;表名列名等标识符需白名单校验;动态dml须严格匹配returning/bulk collect参数与初始化。

EXECUTE IMMEDIATE 拼接字符串直接执行 SQL 就是注入入口
只要把用户输入、配置值或任何不可信来源的数据用 || 拼进 sql_stmt,就等于把 SQL 解析权交给了输入内容。比如:lv_query := 'SELECT name FROM users WHERE id = ' || user_id,当 user_id 是 '1 OR 1=1 --',整条语句就变成 SELECT name FROM users WHERE id = 1 OR 1=1 --',查出全表数据。
Oracle 不会校验拼接后的字符串是否“合理”,只管执行——语法对就跑,不对就报 ORA-00900 或更隐蔽的逻辑越界。
- 单引号嵌套错乱(如
name := 'O''Connor'没转义)→ORA-00917: missing comma - 空值或 NULL 参与拼接 → 整个
sql_stmt变成NULL,执行时报ORA-06502 - 数字/日期格式不一致(如
TO_DATE('2023-01-01', 'YYYY-MM-DD')拼错)→ 运行时报ORA-01843: not a valid month
绑定变量(USING)不是“可选优化”,而是唯一安全路径
USING 子句让 Oracle 在执行前把值和语句结构彻底分离:SQL 文本固定,参数走独立通道。Oracle 自动处理类型转换、空值、引号转义、长度截断等底层细节。
错误写法:lv_query := 'SELECT * FROM emp WHERE deptno = ' || dept_id(拼接)
正确写法:lv_query := 'SELECT * FROM emp WHERE deptno = :1'; EXECUTE IMMEDIATE lv_query INTO rec USING dept_id
- 占位符必须是
:1、:2这类位置式,不能写:dept_id然后指望 Oracle 自动映射变量名 -
USING后只能跟变量或表达式,不能跟字面量(USING 123报错,得先v_id := 123; USING v_id) - 多个相同值要重复传,比如
WHERE col IN (:1, :2)就得USING val, val,少一个就报ORA-01008: not all variables bound
对象名(表名、列名)不能用绑定变量,但也不能裸拼
绑定变量只替代值(value),不替代标识符(identifier)。所以 SELECT * FROM :table_name 语法非法;USING tab_var 也无效。
这类场景必须拼接,但必须加白名单校验:
- 用
USER_TABLES或ALL_OBJECTS查表是否存在:SELECT COUNT(*) INTO cnt FROM user_tables WHERE table_name = UPPER(tab_name) - 禁止传入含点号、双引号、斜杠的字符串(防
SCHEMA.TABLE或带 schema 的跨用户访问) - 列名校验建议走
USER_TAB_COLUMNS,尤其涉及ORDER BY或SELECT列表时 - 拼接后务必用
DBMS_OUTPUT.PUT_LINE(sql_stmt)打印出来,手动粘贴到 SQL*Plus 验证语法
RETURNING INTO 和 BULK COLLECT 容易忽略的陷阱
动态 DML 带 RETURNING 或查多行时,稍不注意就会崩:
-
RETURNING INTO必须配合USING使用,且变量数量、顺序、类型必须和RETURNING列严格一致,否则ORA-06502 -
BULK COLLECT INTO前必须初始化集合:v_list := t_list();,否则v_list.COUNT会报ORA-06531: Reference to uninitialized collection - 查单行必须配
INTO+ 异常块捕获NO_DATA_FOUND和TOO_MANY_ROWS,别指望靠ROWNUM=1掩盖逻辑缺陷 - 大结果集慎用
BULK COLLECT,没加LIMIT容易 OOM;真要分页,优先考虑游标 +FETCH ... BULK COLLECT LIMIT
真正难的不是写对一句 EXECUTE IMMEDIATE,而是所有拼接点都守住边界、所有绑定点都核对顺序、所有对象名都过白名单——漏掉任意一环,安全性和健壮性就归零。










