execute immediate 本身不危险,根本原因是将未过滤的用户输入直接拼入sql字符串;绑定变量仅适用于值,不适用于表名、列名等语法结构,后者必须通过白名单校验。

EXECUTE IMMEDIATE 为什么会触发 SQL 注入
根本原因不是 EXECUTE IMMEDIATE 本身危险,而是你把用户输入直接拼进字符串里再执行。Oracle 不会帮你校验拼出来的 SQL 是否“合法”或“安全”,它只负责执行——哪怕那条 SQL 是 'DROP TABLE users' || :user_input。
常见错误现象:ORA-00911: invalid character 看似是语法错,实际常因未过滤掉分号、注释符(--、/*)导致语句被截断或注入;更隐蔽的是布尔盲注式绕过,比如传入 ' OR 1=1 -- 让条件恒真。
- 所有来自
:bind_variable以外的拼接内容(尤其是||连接的字符串)都必须视为不可信输入 - 动态表名、列名、排序字段等无法用绑定变量的地方,必须走白名单校验,不能靠正则“大概过滤”
- 不要试图用
REPLACE去删分号或单引号——攻击者可用 Unicode 变体、注释嵌套等方式绕过
哪些地方必须用绑定变量,哪些地方不能用
绑定变量(:var)只能替代 SQL 中的**值(value)**,不能替代对象名、关键字、函数名或语法结构。这是 Oracle 的硬限制,不是风格建议。
能用绑定变量的场景:WHERE name = :user_name、INSERT INTO t VALUES (:a, :b)、ORDER BY :sort_col(⚠️注意:这个 :sort_col 实际上是非法的!见下一条)
该系统采用多层模式开发,这个网站主要展示女装的经营,更易于网站的扩展和后期的维护,同时也根据常用的SQL注入手段做出相应的防御以提高网站的安全性,本网站实现了购物车,产品订单管理,产品展示,等等,后台实现了动态权限的管理,客户管理,订单管理以及商品管理等等,前台页面设计精致,后台便于操作等。实现了无限子类的添加,实现了动态权限的管理,支持一下一个人做的辛苦
- WHERE / HAVING 条件中的值、INSERT/UPDATE 的列值、函数参数(如
TO_DATE(:date_str, 'YYYY-MM-DD'))——全都可以且必须用绑定变量 - 表名、列名、
ORDER BY字段、GROUP BY表达式、UNION子句结构——统统不能用绑定变量,Oracle 会报ORA-01027: bind variables not allowed for data definition operations - 想动态
ORDER BY?得用 CASE + 白名单:例如ORDER BY CASE :sort WHEN 'name' THEN name WHEN 'id' THEN id END
动态对象名(表名/列名)的安全校验怎么做
没有银弹,唯一可靠方式是预定义白名单 + 严格字符校验。别信“我只允许字母数字下划线”——Oracle 对象名支持双引号包裹的任意字符(包括空格、中文、连字符),而双引号本身正是注入入口。
正确做法是把允许的表名列名穷举为常量集合,再用 IN 或 CASE 匹配:
IF p_table_name NOT IN ('employees', 'departments', 'projects') THEN
RAISE_APPLICATION_ERROR(-20001, 'Invalid table name');
END IF;- 校验必须在拼接 SQL 字符串之前完成,不能放在 EXECUTE IMMEDIATE 之后
- 避免用
DBMS_ASSERT的SQL_OBJECT_NAME——它只检查是否为合法对象名,不校验权限或业务白名单,且对同义词、带 schema 的名字行为复杂 - 如果必须支持用户自定义表名(如租户隔离场景),应从数据字典查
ALL_TABLES并限定OWNER,而不是信任输入
EXECUTE IMMEDIATE 的性能和调试陷阱
每次执行不同字符串的 EXECUTE IMMEDIATE 都会触发硬解析,和没绑定变量的普通 SQL 一样慢。更麻烦的是,出错时堆栈指向 EXECUTE IMMEDIATE 行号,而不是动态 SQL 内部哪一行——调试成本陡增。
- 把完整 SQL 赋给变量再执行,方便打印调试:
v_sql := 'SELECT * FROM ' || v_table || ' WHERE id = :1'; DBMS_OUTPUT.PUT_LINE(v_sql); EXECUTE IMMEDIATE v_sql INTO v_result USING p_id; - 避免在循环里反复拼接并执行相同结构的 SQL——提取成带绑定变量的静态 SQL,或用
BULK COLLECT+FORALL - 注意
EXECUTE IMMEDIATE默认不继承当前事务上下文,但异常时仍会回滚到最近的保存点;若需精确控制,显式加SAVEPOINT
最易被忽略的一点:动态 SQL 中的注释(--)、分号(;)、引号嵌套,哪怕语法合法,也可能在特定 Oracle 版本或 NLS 设置下表现异常——上线前务必用真实边界值(含特殊字符用户名、含单引号地址)跑集成测试。









