如何让eval()仅支持算术表达式与指定函数?求替代方案
解决eval()安全风险的计算器优化方案
一、限制eval()的执行范围(最小权限原则)
直接使用eval()会继承当前全局命名空间,恶意用户可输入__import__('os').system('rm -rf /')这类代码执行危险操作。通过指定globals和locals参数,能严格限制eval可访问的内容:
- 明确导入允许的函数
替换from mymathmodule import *为精准导入,避免引入不必要的对象:
from mymathmodule import percentagecalc, factorialcalc, factorial, sqrt # 仅导入需要开放的函数 import time
- 构建安全命名空间
创建仅包含允许的内置算术函数和自定义函数的命名空间,彻底隔离危险内置对象:
# 允许使用的内置算术函数 safe_builtins = { 'abs': abs, 'pow': pow, 'round': round, 'sum': sum, } # 组合自定义安全函数到全局命名空间 safe_globals = { '__builtins__': safe_builtins, 'factorial': factorial, 'sqrt': sqrt }
- 修改eval调用
在基础算术模块中,使用安全命名空间调用eval:
elif a == 3: calc = input("Enter Expression:") try: result = eval(calc, safe_globals, {}) print("Expression equals:", result) except Exception as e: print(f"\nInvalid expression: {str(e)}") time.sleep(0.6)
这样用户只能使用你指定的函数和基础算术运算,无法执行任何超出范围的代码。
二、eval()的安全替代方案
如果想彻底规避eval()的风险,推荐使用ast模块解析表达式语法树,手动控制允许的操作,安全性更高:
方案1:基于ast模块的安全求值
import ast from mymathmodule import percentagecalc, factorialcalc, factorial, sqrt import time # 允许的算术操作符 ALLOWED_OPS = { ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Pow, ast.Mod, ast.UAdd, ast.USub } # 允许调用的函数 ALLOWED_FUNCTIONS = { 'factorial': factorial, 'sqrt': sqrt, 'abs': abs, 'pow': pow, 'round': round } def safe_eval(expr): # 解析表达式为语法树 try: tree = ast.parse(expr, mode='eval') except SyntaxError: raise ValueError("Invalid expression syntax") # 遍历语法树,检查所有节点合法性 for node in ast.walk(tree): if isinstance(node, ast.Call): # 禁止调用未授权函数 if not isinstance(node.func, ast.Name) or node.func.id not in ALLOWED_FUNCTIONS: raise ValueError(f"Function '{node.func.id}' is not allowed") elif isinstance(node, ast.Constant): # 允许常量(数字、布尔值等) continue elif type(node) not in ALLOWED_OPS: # 禁止未授权操作 raise ValueError(f"Operation '{type(node).__name__}' is not allowed") # 在安全命名空间中执行表达式 return eval(compile(tree, filename='', mode='eval'), {'__builtins__': None}, ALLOWED_FUNCTIONS) # 修改主循环中的基础算术部分 while True: try: Choose = '''Choose your function: 1: Percentage calculator 2: Factorial calculator 3: Basic arithmetics (+-*/) ''' for character in Choose: print(character, end="", flush=True) time.sleep(0.006) a = int(input(':')) if a == 1: percentagecalc() elif a == 2: factorialcalc() elif a == 3: calc = input("Enter Expression:") try: result = safe_eval(calc) print("Expression equals:", result) except ValueError as e: print(f"\nError: {e}") time.sleep(0.6) else: print("\nOops! Error. Try again...") time.sleep(0.6) except Exception as e: print(f"\nOops! Error: {str(e)}. Try again...") time.sleep(0.6)
该方法通过语法树检查,完全禁止任何未授权的操作和函数调用,安全性远高于直接使用eval()。
方案2:使用第三方库(可选)
如果不想手动实现语法树检查,可使用simpleeval库,它支持配置允许的函数和操作符,使用更简洁:
from simpleeval import SimpleEval # 初始化求值器并添加允许的函数 se = SimpleEval() se.functions['factorial'] = factorial se.functions['sqrt'] = sqrt # 使用时直接调用 result = se.eval(calc)
需先通过pip install simpleeval安装该库。
内容的提问来源于stack exchange,提问作者SK-the-Learner
相关产品推荐
相关产品推荐

