唯一可靠路径是:结构部分用DBMS_ASSERT白名单校验,值部分必须走绑定变量。错误写法含字符串拼接变量或冒号拼接,正确写法为静态SQL字面量加USING绑定;DBMS_ASSERT仅对标识符有效,不可用于值校验,DBMS_SQL需严格绑定与游标管理。
oracle 动态 sql 注入风险不能靠“加个过滤”或“拼完再检查”来解决,唯一可靠路径是:结构部分(表名、列名)用 dbms_assert 白名单式校验,值部分(where 条件值、update 赋值)必须走绑定变量,二者缺一不可。
EXECUTE IMMEDIATE 中绑定变量怎么写才不白写
很多人写了 :1 或 :dept_id 就以为安全了,其实只要变量还在字符串里拼过,就等于没防住。
- ❌ 错误:字符串拼接中混入变量名字面量 ——
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = ' || dept_id - ❌ 错误:冒号被当作文本拼进去 ——
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = :' || '1' USING dept_id(':' || '1'是字符串拼接,不是占位符) - ✅ 正确:SQL 字符串是完整静态字面量,
USING后跟 PL/SQL 变量 ——EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = :1' USING dept_id - 多个参数必须严格按位置或命名匹配:
USING v_dept, v_status对应语句中:1和:2;若用:dept_id,则USING v_dept即可,无需写名字 -
USING不接受表达式,如USING UPPER(name)会报错;需先v_name := UPPER(name);再绑定
DBMS_ASSERT 什么时候有用,什么时候是假安全感
DBMS_ASSERT 不是 SQL 注入的“万能胶”,它只在输入必须符合 Oracle 标识符语法时才起效,且函数选错就等于没校验。
-
DBMS_ASSERT.SIMPLE_SQL_NAME:适合纯字母数字+下划线的列名、别名,但拒绝tenant-abc.orders这类带连字符的合法业务名 -
DBMS_ASSERT.QUALIFIED_SQL_NAME:支持schema.table,但每一段仍要满足SIMPLE_SQL_NAME规则,"HR"."EMPLOYEES"会被拒绝(双引号不合法) -
DBMS_ASSERT.ENQUOTE_NAME:只加双引号、转义内部引号,不做任何合法性判断,输入employees"; DROP TABLE users --仍原样输出 -
DBMS_ASSERT.SQL_OBJECT_NAME:校验是否为数据库中真实存在的对象名(需有 SELECT_CATALOG_ROLE),但无法防止'password' || ' = ''x'' --'这类逻辑注入 - 绝对不要对 WHERE 值(如搜索关键词、用户 ID 字符串)用
DBMS_ASSERT—— 它根本不该出现在这里
DBMS_SQL.PARSE 的高危陷阱和正确姿势
DBMS_SQL.PARSE 比 EXECUTE IMMEDIATE 更底层,也更容易踩坑。老系统里大量 || 拼接 + PARSE 的写法,基本等于敞开大门。
- ❌ 危险模式:
sql_stmt := 'SELECT * FROM ' || user_table || ' WHERE id = ' || user_id; DBMS_SQL.PARSE(v_cursor, sql_stmt, DBMS_SQL.NATIVE);—— 任意拼接都不可接受 -
language_flag若设为V7,会跳过现代语法校验,让非法构造更易成功 - ✅ 安全写法:骨架固定,仅值部分绑定 ——
DBMS_SQL.PARSE(v_cursor, 'SELECT COUNT(*) FROM employees WHERE dept_id = :dept AND status = :stat', DBMS_SQL.NATIVE); - 绑定时变量名必须带冒号前缀:
DBMS_SQL.BIND_VARIABLE(v_cursor, ':dept', p_dept_id),写成':dept_id'或'dept_id'都不会生效 - 用完必须显式
DBMS_SQL.CLOSE_CURSOR(v_cursor),否则游标泄漏导致ORA-01000: maximum open cursors exceeded
最常被忽略的一点是:表名、列名、排序字段这些“结构”输入,既不能进绑定变量,也不能靠正则或 REPLACE 过滤单引号来应付——它们必须经过明确的白名单控制或 DBMS_ASSERT 函数校验;而所有“值”输入,无论看起来多干净,都必须脱离 SQL 字符串上下文,由 Oracle 引擎统一处理类型与转义。两者一旦混淆,防护就形同虚设。


















