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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:39:17