Oracle防SQL注入必须用绑定变量(USING子句),动态SQL字符串须完全静态,结构名需DBMS_ASSERT校验,IN子句用MEMBER OF或固定占位符,权限遵循最小原则。

EXECUTE IMMEDIATE 必须配 USING,不能拼字符串
所有来自外部的值(HTTP参数、表单、游标返回字段等)都不能参与字符串拼接。拼接后执行的 SQL 会被 Oracle 当作完整语句解析,恶意输入如 ' OR 1=1 -- 会直接改写逻辑。
- ❌ 危险写法:
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE name = ''' || user_name || '''' - ✅ 安全写法:
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE name = :n' USING user_name -
:n是占位符,user_name是已声明的 PL/SQL 变量,Oracle 在绑定阶段做类型检查和转义 - USING 后面不能跟表达式,比如
USING UPPER(name)会报错;必须先赋值:l_name := UPPER(name); EXECUTE IMMEDIATE ... USING l_name
IN 子句怎么安全传多个值?
Oracle 不支持 WHERE id IN (:1, :2, :3) 动态绑定任意长度列表,硬拼字符串或试图用 USING 传数组都会触发 ORA-01008: not all variables bound。
- ✅ 推荐做法:用嵌套表 +
MEMBER OFSELECT * FROM orders WHERE order_id MEMBER OF :id_list,绑定时传sys.odcinumberlist(101, 102, 103) - ✅ 简单场景:预设最大数量(如 10),SQL 写死
WHERE id IN (:1, :2, ..., :10),未用参数传NULL,再加AND id IS NOT NULL过滤 - ❌ 别用
LISTAGG拼字符串再查——又回到拼接,高危
表名、列名、排序方向这些能绑定吗?
不能。绑定变量只适用于值(WHERE 条件、SET 赋值),不适用于语法结构。但裸拼 p_table_name 同样危险,必须校验。
- ✅ 表名/列名:用
DBMS_ASSERT.SIMPLE_SQL_NAME(p_table_name)校验后再拼
它只允许字母、数字、_、$、#,长度 ≤ 30,且不是保留字 - ❌ 别用
DBMS_ASSERT.ENQUOTE_NAME当校验函数——它只加双引号,不防注入 - ❌ 排序方向(
ASC/DESC)、点号分隔(schema.table)、空格等需拆开单独白名单判断,SIMPLE_SQL_NAME不覆盖这些
动态 SQL 执行后要不要 COMMIT?
EXECUTE IMMEDIATE 本身不自动提交事务。DML 需显式 COMMIT 或 ROLLBACK;DDL(如 CREATE TABLE)会隐式提交,但会中断当前事务。
- DML 场景(INSERT/UPDATE/DELETE):
EXECUTE IMMEDIATE ...; COMMIT;或在调用方统一控制 - DDL 场景(CREATE/DROP/ALTER):
EXECUTE IMMEDIATE 'CREATE TABLE ...'会立即提交,之前 DML 不可回滚 - 多行查询要用
BULK COLLECT INTO或OPEN FOR游标,INTO只接受单行结果
真正容易被忽略的是:结构名校验和值绑定必须分层处理——表名走 DBMS_ASSERT,值走 USING,两者混用或缺一都会留缺口。没有“差不多安全”的中间态。

















