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

如何安全访问Python嵌套字典并返回数据对应来源路径

问题描述

我们持有多个嵌套字典结构,用户可自由选取不同字典中的字段拼装生成YAML文件,例如可从字典data1中选取某一数据,从字典data2中选取另一数据。
但最终生成的YAML文件中,无法快速定位每个数据的来源,因此需要实现一种简便的嵌套字典数据访问方案,在获取数据值的同时自动附加该数据的完整访问路径,预期效果参考如下示例:

my_data_dict = {
    "data_source_1": {"foo": {"bar": 'data1'}},
    "data_source_2": {"foo": {"bar": 'data2'}},
    "data_source_3": {"foo": {"bar": 'data3'}},
}

>>> my_data_dict["data_source_1"]["foo"]["bar"]
expected : {'data': 'data1', '__source': '["data_source_1"]["foo"]["bar"]'}

原有实现基于*eval()*完成取值,存在严重的任意代码执行安全风险,不适合生产环境使用,原有代码如下:

def get_info(_my_data_dict, keys):
    """Return data in a dictionary with 2 keys : data and __source."""
    result = dict()
    result["data"] = eval(_my_data_dict + keys)
    result["__source"] = str(keys.strip())
    return result

my_data_dict = {
    "data_source_1": {"foo": {"bar": 'data1'}},
    "data_source_2": {"foo": {"bar": 'data2'}},
    "data_source_3": {"foo": {"bar": 'data3'}},
}

>>> print(get_info("my_data_dict", '["data_source_1"]["foo"]["bar"]'))
{'data': 'data_foobar', '__source': '["data_source_1"]["foo"]["bar"]'}
安全实现方案

核心思路是完全抛弃eval(),通过逐层访问字典的方式取值,从根源上避免代码注入风险。提供两种可直接使用的实现:

方案1:键列表传参(最安全,无解析开销)

直接传入按访问顺序排列的键列表,不需要做字符串解析,性能最优,完全没有注入风险,是最推荐的实现方式:

from functools import reduce
import operator

def get_info(data_dict: dict, key_path: list[str]):
    """
    访问嵌套字典,返回取值结果与完整访问路径
    :param data_dict: 待访问的根字典对象
    :param key_path: 按层级顺序排列的键列表,例:["data_source_1", "foo", "bar"]
    """
    # 逐层完成字典取值
    data = reduce(operator.getitem, key_path, data_dict)
    # 拼接标准格式的来源路径
    source_path = "".join(f'["{k}"]' for k in key_path)
    return {"data": data, "__source": source_path}


# 调用示例
my_data_dict = {
    "data_source_1": {"foo": {"bar": 'data1'}},
    "data_source_2": {"foo": {"bar": 'data2'}},
    "data_source_3": {"foo": {"bar": 'data3'}},
}

print(get_info(my_data_dict, ["data_source_1", "foo", "bar"]))
# 输出:{'data': 'data1', '__source': '["data_source_1"]["foo"]["bar"]'}

方案2:兼容原有字符串路径传参

如果需要兼容原有字符串格式的路径输入,使用正则提取路径中的键名,全程不执行任何动态代码,同样可以保证安全:

import re
from functools import reduce
import operator

def get_info(data_dict: dict, key_path_str: str):
    """
    兼容字符串格式路径的安全实现
    :param data_dict: 待访问的根字典对象
    :param key_path_str: 字符串格式访问路径,例:'["data_source_1"]["foo"]["bar"]'
    """
    # 正则提取所有方括号内的键名,无代码执行逻辑
    key_path = re.findall(r'\["([^"]+)"\]', key_path_str.strip())
    if not key_path:
        raise ValueError("键路径格式无效")
    # 逐层取值,路径不存在时抛出原生KeyError,和原生字典访问行为一致
    data = reduce(operator.getitem, key_path, data_dict)
    return {"data": data, "__source": key_path_str.strip()}


# 调用示例
my_data_dict = {
    "data_source_1": {"foo": {"bar": 'data1'}},
    "data_source_2": {"foo": {"bar": 'data2'}},
    "data_source_3": {"foo": {"bar": 'data3'}},
}

print(get_info(my_data_dict, '["data_source_1"]["foo"]["bar"]'))
# 输出:{'data': 'data1', '__source': '["data_source_1"]["foo"]["bar"]'}

补充说明

  • 两种实现都完全规避了eval()带来的代码注入风险,取值逻辑和原生字典访问行为一致
  • 如果嵌套结构中包含列表,只需要调整键解析逻辑,将数字格式的键转为整数类型即可支持索引访问
  • 优先使用方案1的键列表传参方式,从根源上避免字符串解析带来的格式兼容问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 00:18:39