全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-19 10 分钟 ✍️ juanwangdev

Python eval与exec安全使用

evalexec提供动态执行代码的能力,但也带来严重安全风险。正确使用至关重要。

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}))

要点总结

  1. **eval执行表达式返回结果,exec**执行语句不返回
  2. 绝不要直接执行用户输入的代码
  3. 限制globals移除危险内置函数
  4. AST预检查可拦截危险节点
  5. 预编译+受限环境是最安全的组合

📝 发现内容有误?点击此处直接编辑

← 上一篇 Python __new__方法
下一篇 → Python type元类基础
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库