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

Oracle动态列查询存储过程是否存在SQL注入风险?求安全实现方案

你的存储过程存在严重的SQL注入风险!

兄弟,你这个写法确实简洁,但安全隐患可不小——当前实现完全不安全,因为columns参数是直接拼接进SQL语句的,攻击者可以通过构造恶意输入执行任意SQL命令。

举个直观的例子,如果有人调用这个存储过程时传入:

* FROM MySchema.Table1; DROP TABLE MySchema.Table2; --

最终执行的SQL会变成:

SELECT * FROM MySchema.Table1; DROP TABLE MySchema.Table2; -- FROM MySchema.Table1

这会直接删除你的Table2,后果不堪设想。

为什么参数化查询在这里没用?因为SQL的参数化只能处理值,不能处理表名、列名这类标识符,所以你没法把columns作为参数传入,只能通过拼接,但必须先做严格的合法性验证。

正确的实现方式:列名校验+白名单机制

在Oracle中,你可以通过查询数据字典表(比如USER_TAB_COLUMNS)来验证传入的列名是否确实存在于MySchema.Table1中,只有合法的列名才允许拼接。具体步骤如下:

  1. 将传入的columns字符串按逗号拆分成单个列名
  2. 逐个检查每个列名是否在Table1的列列表中
  3. 如果存在非法列名,直接抛出错误;如果全部合法,再拼接成安全的SQL语句

下面是改进后的存储过程示例:

CREATE OR REPLACE PROCEDURE MySchema.Search(columns IN VARCHAR2, res_out OUT SYS_REFCURSOR) IS
    v_valid_columns VARCHAR2(4000) := '';
    v_col           VARCHAR2(128);
    v_start_pos     PLS_INTEGER := 1;
    v_end_pos       PLS_INTEGER;
BEGIN
    -- 处理空列名的异常情况
    IF columns IS NULL OR TRIM(columns) = '' THEN
        RAISE_APPLICATION_ERROR(-20001, '列名参数不能为空');
    END IF;

    -- 拆分传入的列名字符串(按逗号分隔)
    LOOP
        v_end_pos := INSTR(columns, ',', v_start_pos);
        IF v_end_pos = 0 THEN
            v_col := TRIM(SUBSTR(columns, v_start_pos));
            v_start_pos := LENGTH(columns) + 1;
        ELSE
            v_col := TRIM(SUBSTR(columns, v_start_pos, v_end_pos - v_start_pos));
            v_start_pos := v_end_pos + 1;
        END IF;

        -- 验证列名是否存在于Table1中(Oracle字典表默认存大写,所以统一转大写校验)
        BEGIN
            SELECT column_name
            INTO v_col
            FROM USER_TAB_COLUMNS
            WHERE table_name = 'TABLE1'
              AND column_name = UPPER(v_col);

            -- 合法列名加入有效列表
            IF v_valid_columns IS NOT NULL THEN
                v_valid_columns := v_valid_columns || ', ';
            END IF;
            v_valid_columns := v_valid_columns || v_col;
        EXCEPTION
            WHEN NO_DATA_FOUND THEN
                RAISE_APPLICATION_ERROR(-20002, '非法列名: ' || v_col);
        END;

        EXIT WHEN v_start_pos > LENGTH(columns);
    END LOOP;

    -- 拼接并执行安全的动态SQL
    OPEN res_out FOR 'SELECT ' || v_valid_columns || ' FROM MySchema.Table1';
END Search;
/

这个实现的安全优势:

  • 所有传入的列名都必须在Table1的合法列列表中,否则直接阻断执行
  • 自动处理列名的大小写问题(适配Oracle字典表的存储规则)
  • 从根源上避免了恶意SQL注入的可能

如果你的业务场景中允许查询的列是固定的,也可以直接用硬编码白名单的方式(比如提前定义一个列名数组,检查传入列是否在数组内),这种方式性能会更优。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:32:01