必须使用USING绑定所有用户输入值,EXECUTE IMMEDIATE后SQL字符串须完全静态;拼接用户变量即高危;IN子句需用MEMBER OF或预设固定占位符;表名列名等结构信息须白名单校验或DBMS_ASSERT校验。

必须用 USING 绑定所有用户输入的值,拼接字符串就是开后门——没有例外,没有“基本安全”的中间态。
EXECUTE IMMEDIATE 的字符串必须完全静态
只要 EXECUTE IMMEDIATE 后面的 SQL 字符串里出现 || 拼接用户变量(比如 user_name、dept_id),无论加没加单引号、转义或 REPLACE,都算高危。Oracle 不解析拼出来的内容是否“像攻击”,它只管语法合法就执行。
- ❌ 错误:
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE name = ''' || user_name || ''''——user_name是' OR 1=1 --就全表泄露 - ✅ 正确:
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE name = :n' USING user_name——:n是占位符,user_name是纯值,由 Oracle 引擎统一做类型检查和转义 - ⚠️ 注意:
USING后只能跟 PL/SQL 变量(如VARCHAR2、NUMBER),不能是表达式;USING UPPER(name)会报错,得先l_name := UPPER(name);再USING l_name
IN 子句传多个值不能靠拼字符串
WHERE id IN (:a, :b, :c) 看似合理,但 Oracle 不支持运行时动态展开绑定列表。硬塞变长数组会触发 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,:3,:4,:5,:6,:7,:8,:9,:10),未用位置传NULL,再加AND id IS NOT NULL - ❌ 千万别:
LISTAGG拼字符串再用INSTR查——这等于绕开绑定,Oracle 仍执行拼好的字符串
表名、列名等结构信息不能绑,但也不能裸拼
绑定变量只保护“值”,不保护对象名。直接拼 'SELECT * FROM ' || user_table 或 'ORDER BY ' || sort_col 同样危险,必须分层校验。
- ✅ 白名单优先:把允许的表名存在配置表或
SYS_CONTEXT中,查表确认后再拼 - ✅
DBMS_ASSERT.SIMPLE_SQL_NAME辅助校验:safe_table := DBMS_ASSERT.SIMPLE_SQL_NAME(p_table_name)—— 它会拦住空格、分号、双横线、点号等非法字符,但不验证对象是否存在 - ⚠️ 注意:
DBMS_ASSERT.SQL_OBJECT_NAME更严格,但要求输入已真实存在且有权限;它不防逻辑注入(如"employees"; DROP TABLE logs --可能通过校验) - ? 排序方向(
ASC/DESC)要拆开单独校验,SIMPLE_SQL_NAME不接受空格
.NET 中 OracleCommand.CommandText 必须是静态字符串
在 .NET 里,OracleParameter 生效的前提是:整个 CommandText 本身不含任何用户输入拼接。一旦 C# 层用 $"SELECT ... WHERE name = '{name}'" 构造 SQL,后续加 Parameters.Add 就完全无效。
- ❌ 错误:
cmd.CommandText = $"SELECT * FROM users WHERE name = '{name}'";—— 注入发生在字符串插值阶段 - ✅ 正确:
cmd.CommandText = "SELECT * FROM users WHERE name = :name";,再cmd.Parameters.Add(new OracleParameter("name", name)) - ⚠️ 命名绑定需严格匹配:SQL 中写
:name,参数名就得是"name";若用位置绑定(:1),顺序不能错
最常被忽略的一点:绑定变量的安全性完全依赖 SQL 字符串的静态性。哪怕多一个空格、换行或不可见字符导致冒号被当成字面量(比如 ' :id '),Oracle 就不再识别为占位符——此时 USING 形同虚设。


















