You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何清理用户输入并在DDL/DCL中使用以避免SQL注入及DBMS_ASSERT用法

问题背景与疑问

我有一个用于创建新用户、设置/修改用户密码、给用户授予角色的PL/SQL包。包内函数会接收用户输入并和用户表校验:如果是现有用户就修改密码,新用户则创建。目前所有存储过程里,fp_userid都是直接拼到dbms_sql.parse语句里;我试过用execute immediate,但还是得拼接值,感觉没法绑定变量。想请教:

  1. DDL、DCL语句能不能绑定变量?
  2. 怎么清理用户输入避免SQL注入?
  3. 这个场景下怎么用dbms_assert包?

相关代码

检查用户存在的函数代码

Function fn_userexists(fp_userid IN varchar2)
return boolean
is
   cursor c is 
     select userid, password,userrole,deleted from user_db where userid = fp_userid;

r_user c%rowtype;
begin
open c;
fetch c into r_user;
if c%notfound then
return false;
elsif r_user.deleted = 'Y' then
if fn_oracleuserexists (fp_userid) then
pr_dropuser (fp_userid);
end if;
close c;
delete user_db where userid = fp_userid;
return false;
elsif not fn_oracleuserexists (fp_userid) then
pr_createuser(fp_userid, fn_hid(r_user.password,fp_userid));
pr_grantuserrole(fp_userid, 'SMC_USER');
pr_grantuserrole(fp_userid, r_user.userrole);
return true;
end if;
return true;
exception 
when others then
return false;
end;

存在注入风险的execute immediate代码

execute immediate 'CREATE USER "'||pp_userid||'" IDENTIFIED BY "' || pp_password || '"
                               DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP';

解答

1. DDL/DCL语句能否绑定变量?

Oracle的DDL(比如CREATE USER、GRANT)和DCL语句不支持绑定变量。这类语句会直接修改数据字典,Oracle需要在解析阶段就明确对象名称(比如用户名),因此没法用绑定变量替代对象名、密码这类内容,只能通过字符串拼接生成语句。

2. 清理用户输入避免SQL注入的方法

既然必须拼接,就得严格做输入校验和清理:

  • 限制输入长度:Oracle用户名最长30字符(12c+可扩展至128,但建议按业务需求设更短限制),密码也有长度上限,先截断或直接拒绝超长输入。
  • 过滤非法字符:Oracle用户名默认只允许字母、数字、下划线、#、$,如果输入包含空格、引号、特殊符号,直接拒绝(除非业务必须支持带特殊字符的用户名,这种情况要做转义)。
  • 转义特殊字符:如果允许带特殊字符的用户名(必须用双引号包裹的场景),要把输入里的双引号转义成两个双引号,避免闭合拼接的引号引发注入。比如把user"name转成user""name。

3. 如何使用dbms_assert包

dbms_assert是Oracle官方提供的防SQL注入工具包,针对你的场景,常用函数如下:

dbms_assert.simple_sql_name

用于校验输入是否为合法的SQL标识符(比如用户名),如果输入包含非法字符会直接抛出异常。用法示例:

declare
  v_valid_userid varchar2(128);
  v_valid_password varchar2(128);
begin
  -- 校验并格式化用户名
  v_valid_userid := dbms_assert.simple_sql_name(pp_userid);
  -- 校验并格式化密码(用双引号包裹的场景)
  v_valid_password := dbms_assert.enquote_name(pp_password, false);
  
  execute immediate 'CREATE USER '||v_valid_userid||' IDENTIFIED BY ' || v_valid_password || '
                               DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP';
end;

这个函数会自动处理双引号转义,确保拼接后的语句不会被注入。

dbms_assert.enquote_literal/dbms_assert.enquote_name

  • enquote_literal:把输入用单引号包裹,同时转义输入里的单引号,适合处理普通字符串值。
  • enquote_name:把输入用双引号包裹,转义内部的双引号,适合处理需要双引号的对象名或密码。

注意事项

调用dbms_assert函数时要捕获异常:如果输入不合法,函数会抛出ORA-44002错误,你可以在代码里捕获这个异常,返回错误提示或直接拒绝操作。

另外,你的fn_userexists函数有几个小问题需要修正:

  • 游标c的定义末尾少了分号;
  • fn_oracleuserexists和pr_dropuser的参数写错成fp_user,应该是fp_userid;
  • delete语句里的fb_userid应该是fp_userid;
  • 异常处理里的when other要改成when others。

内容的提问来源于stack exchange,提问作者Deva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 18:57:05