Python eval与exec安全使用
eval和exec提供动态执行代码的能力,但也带来严重安全风险。正确使用至关重要。
eval 与 exec 区别
Python
# eval: 执行表达式,返回结果
result = eval('2 + 3 * 4')
print(result) # 14
# exec: 执行语句,不返回结果
exec('x = 10; print(x)') # 10
# eval 只能处理表达式
# eval('x = 10') # SyntaxError
# exec 可以处理完整代码块
code = "
def greet(name):
return f"Hello, {name}"
"
exec(code)
print(greet("World")) # Hello from World
| 函数 | 用途 | 返回值 | 适用范围 |
|---|---|---|---|
| eval | 表达式求值 | 结果值 | 单行表达式 |
| exec | 语句执行 | None | 多行代码块 |
| compile | 编译源码 | Code Object | 预编译优化 |
安全风险分析
Python
# 危险示例:接受用户输入直接执行
user_input = "__import__('os').system('rm -rf /')"
# eval(user_input) # 极危险!
# 常见攻击模式:
# 1. 导入危险模块:__import__('os').system('...')
# 2. 访问文件:open('/etc/passwd').read()
# 3. 获取环境:__import__('os').environ
# 4. 执行任意代码:exec(...)
Python
# 内置函数的潜在危险
dangerous_builtins = [
'open', 'exec', 'eval', 'compile',
'__import__', 'input', 'breakpoint',
'getattr', 'setattr', 'delattr',
'globals', 'locals',
]
# 这些函数可以被滥用访问系统资源
限制执行环境
Python
# 方法1:限制 globals 和 locals
safe_globals = {
'__builtins__': {
'abs': abs,
'min': min,
'max': max,
'sum': sum,
'len': len,
'range': range,
'list': list,
'dict': dict,
}
}
# 只允许数学计算
expression = "abs(-5) + max(1, 2, 3)"
result = eval(expression, safe_globals, {})
print(result) # 8
# 尝试危险操作会失败
# eval("__import__('os')", safe_globals, {}) # NameError
Python
# 方法2:完全移除 builtins
safe_globals = {'__builtins__': {}}
safe_globals.update({
'abs': abs,
'min': min,
'max': max,
})
# eval("abs(-10)", safe_globals) # 10
# eval("open('file')", safe_globals) # NameError
Python
# 方法3:自定义安全执行器
class SafeExecutor:
"受限执行环境"
ALLOWED_BUILTINS = {
'abs': abs, 'all': all, 'any': any,
'bool': bool, 'chr': chr, 'ord': ord,
'float': float, 'int': int, 'str': str,
'len': len, 'list': list, 'tuple': tuple,
'dict': dict, 'set': set,
'min': min, 'max': max, 'sum': sum,
'range': range, 'enumerate': enumerate,
'zip': zip, 'sorted': sorted, 'reversed': reversed,
'isinstance': isinstance, 'issubclass': issubclass,
}
def __init__(self):
self.globals = {'__builtins__': self.ALLOWED_BUILTINS}
def eval(self, expression, locals=None):
"安全执行表达式"
return eval(expression, self.globals, locals or {})
def exec(self, code, locals=None):
"安全执行代码"
exec(code, self.globals, locals or {})
executor = SafeExecutor()
print(executor.eval('abs(-5) + max(1, 2)')) # 7
AST预检查
Python
import ast
class SafeASTChecker(ast.NodeVisitor):
"AST安全检查器"
ALLOWED_NODES = {
ast.Expression, ast.Module,
ast.BinOp, ast.UnaryOp, ast.Compare,
ast.Constant, ast.Name, ast.List, ast.Tuple, ast.Dict,
ast.Call, ast.Attribute,
ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod,
ast.Eq, ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE,
ast.And, ast.Or, ast.Not,
}
FORBIDDEN_NAMES = {'eval', 'exec', 'compile', 'open', '__import__'}
def __init__(self):
self.errors = []
def visit(self, node):
if node.__class__ not in self.ALLOWED_NODES:
self.errors.append(f"禁止节点: {node.__class__.__name__}")
return super().visit(node)
def visit_Name(self, node):
if node.id in self.FORBIDDEN_NAMES:
self.errors.append(f"禁止名称: {node.id}")
self.generic_visit(node)
def visit_Call(self, node):
if isinstance(node.func, ast.Name):
if node.func.id in self.FORBIDDEN_NAMES:
self.errors.append(f"禁止调用: {node.func.id}")
self.generic_visit(node)
def safe_eval(expression):
"带AST检查的安全执行"
tree = ast.parse(expression, mode='eval')
checker = SafeASTChecker()
checker.visit(tree)
if checker.errors:
raise SecurityError(f"安全检查失败: {checker.errors}")
safe_globals = {'__builtins__': SafeExecutor.ALLOWED_BUILTINS}
return eval(expression, safe_globals)
# 使用
print(safe_eval('2 + 3 * 4')) # 14
# safe_eval("__import__('os')") # SecurityError
compile + restricted globals
Python
# 预编译提高效率
import ast
code = compile('abs(x) + max(y, z)', '<string>', 'eval')
safe_globals = {'__builtins__': {'abs': abs, 'max': max}}
safe_locals = {'x': -5, 'y': 1, 'z': 10}
result = eval(code, safe_globals, safe_locals)
print(result) # 15
模板表达式安全处理
Python
# 安全模板变量替换
class SafeTemplate:
"安全模板引擎"
def __init__(self, allowed_vars):
self.allowed_vars = allowed_vars
self.safe_globals = {'__builtins__': {}}
def render(self, template, context):
"渲染模板,只允许访问allowed_vars"
# 验证上下文
safe_locals = {}
for key in context:
if key in self.allowed_vars:
safe_locals[key] = context[key]
# 替换变量引用
# template: "{name} has {count} items"
# 转换为安全表达式
result = template
for key, value in safe_locals.items():
result = result.replace(f"{{{key}}}", str(value))
return result
template = SafeTemplate(['name', 'count'])
print(template.render("{name} has {count} items", {'name': 'Alice', 'count': 5}))
要点总结
- **
eval执行表达式返回结果,exec**执行语句不返回 - 绝不要直接执行用户输入的代码
- 限制globals移除危险内置函数
- AST预检查可拦截危险节点
- 预编译+受限环境是最安全的组合
📝 发现内容有误?点击此处直接编辑