能否在DBMS_SQL.PARSE中用绑定变量防SQL注入?DDL场景咨询
技术指引:DDL动态SQL的SQL注入规避与Checkmarx告警处理
核心结论:DDL无法用绑定变量替代对象名
Oracle的绑定变量仅支持数据值的绑定(比如SELECT/WHERE子句中的条件值),对于DDL中的对象标识符(如用户名、表名、索引名等属于SQL语法结构的部分),无法通过绑定变量动态传入,因此直接用绑定变量解决DDL的字符串拼接问题不可行。
针对DDL动态SQL的安全方案
1. 白名单校验(最安全的方式)
DDL属于高风险操作,输入的对象名(如示例中的userid)应处于业务可控范围,先通过白名单验证确保输入合法:
- 校验输入的userid是否存在于合法用户列表(比如查询
DBA_USERS视图确认用户存在) - 或者从业务系统的用户管理表中验证该用户是允许被删除的对象
示例代码(加入白名单校验):
PROCEDURE drop_user_proc(p_userid IN VARCHAR2) IS v_exists NUMBER; v_sqlstatement VARCHAR2(1000); v_cursor INTEGER; BEGIN -- 白名单校验:确认用户存在 SELECT COUNT(1) INTO v_exists FROM DBA_USERS WHERE USERNAME = UPPER(p_userid); IF v_exists = 0 THEN RAISE_APPLICATION_ERROR(-20001, '用户不存在,无法执行删除操作'); END IF; v_cursor := DBMS_SQL.open_cursor; v_sqlstatement := 'DROP USER ' || UPPER(p_userid); DBMS_SQL.parse(v_cursor, v_sqlstatement, DBMS_SQL.NATIVE); DBMS_SQL.close_cursor(v_cursor); END;
2. 使用DBMS_ASSERT包做标识符安全校验
如果无法用白名单,必须允许动态对象名,使用Oracle官方提供的DBMS_ASSERT包校验输入的标识符,避免注入风险:
DBMS_ASSERT.SIMPLE_SQL_NAME:校验输入是否符合Oracle简单标识符规范DBMS_ASSERT.ENQUOTE_NAME:将标识符转为带引号的合法形式(支持包含特殊字符的标识符)
示例代码(使用DBMS_ASSERT):
PROCEDURE drop_user_proc(p_userid IN VARCHAR2) IS v_safe_userid VARCHAR2(30); v_sqlstatement VARCHAR2(1000); v_cursor INTEGER; BEGIN -- 校验并转义用户名,确保合法 v_safe_userid := DBMS_ASSERT.SIMPLE_SQL_NAME(p_userid); v_cursor := DBMS_SQL.open_cursor; v_sqlstatement := 'DROP USER ' || v_safe_userid; DBMS_SQL.parse(v_cursor, v_sqlstatement, DBMS_SQL.NATIVE); DBMS_SQL.close_cursor(v_cursor); END;
3. 简化动态SQL写法(替代DBMS_SQL)
对于简单的DDL操作,推荐使用EXECUTE IMMEDIATE替代DBMS_SQL,代码更简洁,同时配合上述安全校验:
PROCEDURE drop_user_proc(p_userid IN VARCHAR2) IS v_safe_userid VARCHAR2(30); BEGIN v_safe_userid := DBMS_ASSERT.SIMPLE_SQL_NAME(p_userid); EXECUTE IMMEDIATE 'DROP USER ' || v_safe_userid; END;
关于Checkmarx告警的处理
Checkmarx会基于“字符串拼接动态SQL”的规则触发告警,只要你实现了上述任意一种安全校验(白名单/DBMS_ASSERT),可以:
- 向Checkmarx提交误报申诉,提供安全校验的代码说明
- 调整Checkmarx的扫描规则,对包含
DBMS_ASSERT或白名单校验的动态SQL放行
内容的提问来源于stack exchange,提问作者Deva
相关产品推荐
相关产品推荐

