如何安全对包含列表索引的Python表达式进行求值?
报错原因
ast.literal_eval 仅支持解析Python原生字面量(数字、字符串、列表、字典、元组、布尔值、None等),不支持任何运算、索引、属性访问等操作,因此带有[1]索引操作的表达式会被判定为非法节点,抛出ValueError。
解决方案
方案1:自定义AST白名单解析(无依赖、最安全)
通过解析表达式为抽象语法树,仅放行你需要的「字面量构造+下标索引」类节点,完全禁止其他所有语法特性,不存在任意代码执行风险,适配Python全版本:
import ast def safe_eval_literal_with_index(expr: str): # 定义允许的AST节点类型白名单 allowed_nodes = ( ast.Expression, ast.Module, ast.Constant, ast.NameConstant, # 字面量值兼容 ast.List, ast.Tuple, ast.Dict, ast.Set, # 容器字面量 ast.Subscript, ast.Index # 下标操作兼容 ) # 解析表达式为AST ast_tree = ast.parse(expr, mode="eval") # 遍历所有节点校验合法性 for node in ast.walk(ast_tree): if not isinstance(node, allowed_nodes): raise ValueError(f"非法语法:不允许{type(node).__name__}操作") # 编译执行校验后的AST return eval(compile(ast_tree, filename="<safe_expr>", mode="eval"))
使用示例:
# 正常支持的场景 print(safe_eval_literal_with_index('[1, 2, 3][1]')) # 输出 2 print(safe_eval_literal_with_index('({"a":1, "b":2})["b"]')) # 输出 2 print(safe_eval_literal_with_index('(10, 20, 30)[-1]')) # 输出 30 # 危险操作会被拦截 safe_eval_literal_with_index('__import__("os").system("rm -rf /")') # 直接抛出ValueError
如果需要支持切片操作,只需要把ast.Slice添加到allowed_nodes白名单中即可。
方案2:第三方库simpleeval(功能更丰富)
如果需要支持更多简单操作(四则运算、属性访问、自定义函数白名单等),可以直接使用成熟的simpleeval库,它内部基于AST白名单实现,默认禁用所有危险操作:
- 安装依赖
pip install simpleeval
- 使用示例
from simpleeval import simple_eval print(simple_eval('[1, 2, 3][1]')) # 输出 2
内容的提问来源于stack exchange,提问作者mathfux
相关产品推荐
相关产品推荐

