不能用 eval 执行用户输入,因其会直接执行任意代码、无沙箱限制;应优先用 ast.literal_eval 处理安全字面量,必要时用 simpleeval 或自定义 AST 遍历限制运算符和节点。
eval 执行用户输入eval 会直接在当前作用域执行任意 Python 代码,哪怕只是传入 "__import__('os').system('rm -rf /')" 这样的字符串,也可能触发系统调用、文件操作或网络请求。它不区分“表达式”和“语句”,也不限制内置函数访问,本质上不是沙箱——只是把字符串当代码扔进解释器里跑一遍。
真实场景中,用户可能故意输入 "2 + (lambda: exec('print(1)'))()" 绕过简单关键词过滤;或者利用 getattr、__builtins__ 等反射机制逃逸限制。靠字符串黑名单/白名单基本防不住。
ast.literal_eval 处理安全子集ast.literal_eval 只允许基础字面量:数字、字符串、元组、列表、字典、布尔值、None。它不会执行函数调用、属性访问、运算符重载,也不会触发任何副作用。
"[1, 2, {'x': 3.14}]")"2 + 3")、比较("5 > 3")、函数调用("len([1,2])")ValueEr
ror 或 SyntaxError,不是 Exception 的宽泛捕获示例:
import ast
try:
result = ast.literal_eval("[1, 2, {'a': True}]")
except (ValueError, SyntaxError) as e:
# 安全失败,无副作用
print("Invalid literal:", e)
simpleeval 或自定义 AST 遍历如果必须支持 "2 * x + 1" 这类带变量和运算的表达式,ast.literal_eval 不够用,但又不想自己从头写解析器,推荐轻量库 simpleeval:
se.eval("a + b", names={"a": 10, "b": 20})
lambda、import、exec、下标以外的属性访问(如 obj.method)不用第三方库时,可基于 ast.Expression + 白名单节点遍历实现最小计算器,重点拦截 ast.Call、ast.Attribute、ast.Subscript(除非明确允许数组索引)等危险节点。
即使用了 simpleeval 或自定义 AST,也要警惕用户构造边缘输入:
"1 if True else __import__('sys').exit()" → 检查是否允许条件表达式,或禁用 ast.IfExp
"'a' * 10**6" → 限制字符串生成长度,或在遍历时统计总字符估算量"[i for i in range(10**6)]" → 禁用列表推导式(ast.ListComp),或限制循环模拟次数9**9**5 → 在访客节点时检查数字大小和运算符组合真正安全的表达式求值没有银弹。越接近通用计算能力,防御成本越高。多数业务场景其实只需要 ast.literal_eval 或固定几个运算符的白名单——先想清楚“用户到底需要算什么”,再决定沙箱复杂度。