Oracle Apex SQL注入防护:如何加固含V_NAME的PL/SQL函数?
嘿,我来帮你搞定这个SQL注入的问题!你的PL/SQL函数里的核心风险点确实是直接把V_NAME拼到动态SQL里——哪怕这个值是从数据库表查出来的,一旦MST_SEQUENCE_DETAIL表的KEYSEQ字段被恶意篡改(比如插入带注入逻辑的内容),就会触发安全问题。下面是几种靠谱的加固方案,从直接到严谨给你拆解:
方案1:用DBMS_ASSERT验证序列名称(最直接的修复)
Oracle自带的DBMS_ASSERT包专门用来校验SQL对象名的合法性,能直接阻断恶意注入内容的执行。我们可以用它先清洗V_NAME的值,确保它是一个符合规则的序列名:
FUNCTION "GET_SEQUENCE" (P_BID VARCHAR2, P_PSC VARCHAR2) RETURN NUMBER AS TYPE T_HASIL IS TABLE OF NUMBER; V_HASIL T_HASIL; V_NAME VARCHAR2(30); V_SQL LONG; V_VALID_SEQ_NAME VARCHAR2(30); -- 存储验证后的合法序列名 BEGIN SELECT KEYSEQ INTO V_NAME FROM MST_SEQUENCE_DETAIL Tbl WHERE BRANCHCODE=P_BID AND KEYCODE=P_PSC AND YEAR = TO_CHAR(SYSDATE,'RRRR'); -- 关键:用SIMPLE_SQL_NAME验证序列名格式,非法会抛出ORA-44002异常 V_VALID_SEQ_NAME := DBMS_ASSERT.SIMPLE_SQL_NAME(V_NAME); V_SQL := 'SELECT ' || V_VALID_SEQ_NAME || '.NEXTVAL FROM DUAL'; EXECUTE IMMEDIATE V_SQL BULK COLLECT INTO V_HASIL; RETURN V_HASIL(1); EXCEPTION WHEN NO_DATA_FOUND THEN -- 处理找不到对应序列配置的场景 RETURN NULL; WHEN OTHERS THEN -- 可以自定义异常提示,或者直接抛出原异常 RAISE; END;
DBMS_ASSERT.SIMPLE_SQL_NAME会严格检查输入是否符合Oracle对象名的规则:不允许包含分号、注释符、注入语句(比如; DROP TABLE XXX),一旦发现非法内容直接抛出异常,从根源上阻断注入。
方案2:额外验证序列是否存在(更严谨的安全层)
除了格式校验,我们还可以额外检查这个序列是否真的存在于数据库中,避免执行无效的动态SQL,同时进一步降低风险:
FUNCTION "GET_SEQUENCE" (P_BID VARCHAR2, P_PSC VARCHAR2) RETURN NUMBER AS TYPE T_HASIL IS TABLE OF NUMBER; V_HASIL T_HASIL; V_NAME VARCHAR2(30); V_SQL LONG; V_SEQ_EXISTS NUMBER; BEGIN SELECT KEYSEQ INTO V_NAME FROM MST_SEQUENCE_DETAIL Tbl WHERE BRANCHCODE=P_BID AND KEYCODE=P_PSC AND YEAR = TO_CHAR(SYSDATE,'RRRR'); -- 先做格式校验 V_NAME := DBMS_ASSERT.SIMPLE_SQL_NAME(V_NAME); -- 检查序列是否存在于当前用户的序列列表中(如果是其他用户的序列,用ALL_SEQUENCES) SELECT COUNT(1) INTO V_SEQ_EXISTS FROM USER_SEQUENCES WHERE SEQUENCE_NAME = V_NAME; IF V_SEQ_EXISTS = 0 THEN -- 自定义异常提示 RAISE_APPLICATION_ERROR(-20001, 'Sequence ' || V_NAME || ' does not exist in the database'); END IF; V_SQL := 'SELECT ' || V_NAME || '.NEXTVAL FROM DUAL'; EXECUTE IMMEDIATE V_SQL BULK COLLECT INTO V_HASIL; RETURN V_HASIL(1); EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; WHEN OTHERS THEN RAISE; END;
这个方案相当于给函数加了双重保险:既确保序列名格式合法,又确保它是真实存在的有效序列,完全杜绝了恶意构造的序列名带来的风险。
注意:绑定变量在这里不适用
很多人第一反应会想到用绑定变量,但要注意:绑定变量只能用于SQL语句中的值(比如WHERE id = :var),不能用于对象名(序列、表、列名等),所以这个场景下绑定变量帮不上忙,别踩这个坑!
额外建议:最小权限原则
给执行这个函数的数据库用户分配最小权限:只需要授予该用户执行目标序列的权限(GRANT SELECT ON 序列名 TO 用户名;),不要给不必要的高权限(比如DBA、CREATE TABLE等),进一步降低安全风险。
内容的提问来源于stack exchange,提问作者potitit

