如何清理用户输入并在DDL/DCL中使用以避免SQL注入及DBMS_ASSERT用法
问题背景与疑问
我有一个用于创建新用户、设置/修改用户密码、给用户授予角色的PL/SQL包。包内函数会接收用户输入并和用户表校验:如果是现有用户就修改密码,新用户则创建。目前所有存储过程里,fp_userid都是直接拼到dbms_sql.parse语句里;我试过用execute immediate,但还是得拼接值,感觉没法绑定变量。想请教:
- DDL、DCL语句能不能绑定变量?
- 怎么清理用户输入避免SQL注入?
- 这个场景下怎么用
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
相关产品推荐
相关产品推荐

