如何防止用户篡改Oracle APEX Popup LOV值并确保数据合法入库?
Oracle APEX 20.1 Popup LOV 篡改值校验问题
我使用Oracle APEX 20.1.0.00.13版本,遇到Popup LOV组件的安全校验问题:
- 创建了禁用手动输入的Popup LOV页面项
P9000_MEM_ITEM,采用SQL查询定义LOV:
SELECT MEM_NAME || ' (' || MEM_ID || ')' AS DISPLAY_VALUE, MEM_ID AS RETURN_VALUE FROM MEMORY_IDS WHERE MEM_STATUS = 'ENABLE' ORDER BY MEM_ID;
- 页面渲染后,APEX生成隐藏输入元素存储选中值:
<input type="hidden" name="P9000_MEM_ITEM" id="P9000_MEM_ITEM_HIDDENVALUE" value="ABCDEF">
- 恶意用户可通过浏览器开发者工具修改隐藏值为无效值(如
MEM_STATUS为DISABLED的MEM_ID),APEX会直接将篡改后的值存入数据库,未做LOV校验。
核心挑战:
- 问题覆盖应用中1000多个页面的所有Popup LOV;
- 部分LOV依赖其他页面项(如
MEM_STATUS = 'ENABLE' AND MEM_USER = :P9000_ANOTHER_ITEM),逻辑复杂。
我尝试编写验证查询检查提交值是否合法,但无法动态复用LOV查询,也不清楚APEX如何在运行时动态获取LOV查询并替换页面项引用:
SELECT COUNT(*) FROM DUAL WHERE :P9000_MEM_ITEM IN (/* Popup LOV Query */);
请问如何防止用户篡改Popup LOV值,确保仅合法、符合LOV查询规则的值存入数据库?
解决方案
1. 利用APEX内置LOV验证(推荐,高复用性)
APEX自带基于LOV的内置验证,无需重复编写SQL:
- 给目标Popup LOV页面项添加验证,类型选择「值在列表中」
- 验证源选择「基于列表的验证」,直接关联该页面项对应的LOV(共享组件LOV或页面级LOV均可)
- 勾选「使用LOV的WHERE子句」,APEX会自动解析LOV中的绑定变量(如
:P9000_ANOTHER_ITEM),确保校验逻辑与LOV完全一致
优势:
- 完全复用现有LOV定义,无需维护重复SQL
- 自动处理依赖页面项的参数替换
- 针对1000多个页面,可通过APEX应用导出/批量编辑(或元数据表批量生成)快速部署,避免手动逐个添加
2. 自定义通用PL/SQL验证函数(适合批量处理)
若需更灵活控制,可编写通用PL/SQL函数,通过APEX元数据动态获取LOV查询并执行校验:
FUNCTION validate_popup_lov(p_item_name IN VARCHAR2, p_page_id IN NUMBER DEFAULT apex_application.g_flow_id) RETURN BOOLEAN IS l_lov_query VARCHAR2(32767); l_return_value VARCHAR2(4000) := apex_util.get_session_state(p_item_name); l_count NUMBER; BEGIN -- 获取页面级LOV定义 SELECT lov_definition INTO l_lov_query FROM apex_application_page_items WHERE application_id = apex_application.g_flow_id AND page_id = p_page_id AND item_name = p_item_name; -- 若为共享组件LOV,切换到共享LOV表查询 IF l_lov_query LIKE '%SHARED:%' THEN SELECT lov_query INTO l_lov_query FROM apex_application_lovs WHERE application_id = apex_application.g_flow_id AND lov_name = REPLACE(l_lov_query, 'SHARED:', ''); END IF; -- 提取LOV中的RETURN_VALUE查询部分,添加值校验条件 l_lov_query := REGEXP_REPLACE(l_lov_query, 'SELECT.*AS DISPLAY_VALUE,', 'SELECT RETURN_VALUE') || ' WHERE RETURN_VALUE = :P_VALUE'; l_lov_query := REGEXP_REPLACE(l_lov_query, 'ORDER BY.*$', ''); -- 动态执行校验 EXECUTE IMMEDIATE l_lov_query INTO l_count USING l_return_value; RETURN l_count > 0; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN FALSE; END;
给每个Popup LOV页面项添加PL/SQL函数体返回布尔值类型的验证,调用该函数:
RETURN validate_popup_lov('P9000_MEM_ITEM', 9000);
注意:
- 需确保函数有权限访问APEX元数据表(
apex_application_page_items、apex_application_lovs) - 动态SQL需正确处理绑定变量,避免注入风险
- 可结合APEX CLI或SQL脚本批量生成所有Popup LOV的验证
3. 数据库层面约束(最后防线)
即使前端和APEX层面做了校验,数据库约束仍能作为最后保障:
- 针对存储LOV值的字段,添加CHECK约束或外键约束(若值来自固定表)
- 创建触发器,在插入/更新时校验值是否符合LOV规则
示例触发器(针对MEMORY_IDS表的引用):
CREATE OR REPLACE TRIGGER trg_check_mem_id_valid BEFORE INSERT OR UPDATE OF mem_id ON your_target_table FOR EACH ROW DECLARE l_status VARCHAR2(10); BEGIN SELECT mem_status INTO l_status FROM memory_ids WHERE mem_id = :new.mem_id; IF l_status != 'ENABLE' THEN RAISE_APPLICATION_ERROR(-20001, '无效的内存ID'); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE_APPLICATION_ERROR(-20002, '内存ID不存在'); END;
该方法可防止绕过APEX校验的非法数据(如直接通过SQL插入)
4. 前端增强校验(辅助手段)
虽无法完全防止篡改,但可增加恶意用户操作成本:
- 给Popup LOV添加前端事件监听,禁止直接修改隐藏字段
- 使用APEX的
apex.itemAPI监听值变化,确保仅LOV选择的值被提交
页面加载时执行的JavaScript示例:
// 禁止修改Popup LOV的隐藏字段 const hiddenInput = document.getElementById('P9000_MEM_ITEM_HIDDENVALUE'); let lastValidValue = hiddenInput.value; hiddenInput.addEventListener('change', function() { this.value = lastValidValue; }); // 监听LOV选择事件,更新合法值 apex.item('P9000_MEM_ITEM').on('change', function() { lastValidValue = this.getValue(); });
内容的提问来源于stack exchange,提问作者Farshid
相关产品推荐
相关产品推荐

