控制台Blackjack程序:eval安全风险分析及替代方案问询
控制台Blackjack程序的输入评估安全问题与替代方案
背景
我正在开发一款用于娱乐和学习的控制台版Blackjack程序,以下是我用于评估用户输入对应条件的解决方案:
负责处理休眠时长和用户输入的函数所在文件已导入主程序:
import consoleControl as cs def console_control(string, n_sec, return_flag, break_check, condition, wrapper=str): # 函数实现部分
当需要返回结果时,return_flag设为True;若传入condition,则执行以下代码:
if condition: while True: try: # 仅用于数值类型 userIo = wrapper(input(string)) if eval(str(userIo) + condition): return userIo else: return None except ValueError: return None
首次成功运行时的函数调用示例:
# 获取正数值 def get_bankRoll(): while True: # 传入console_control()的条件 condition = '> 0' try: while True: string = 'How much money will you be gamblin\'... player? \nEuros: ' cash = hud_control(0, None, None, None, None, [string, None, True, None, condition, float]) # 后续代码省略
调用至包含固定ASCII字符及其他打印语句的打印例程:
# 包含固定ASCII字符及其他打印语句的打印例程 def hud_control(hud, cash, game_phase, hand_display_args, new_line, control_args): # 为简化展示,中间代码已移除 if hud == 0: pass else: pass if new_line: for x in range(new_line): print('\n') if control_args: if control_args[-1]: return cs.console_control(*control_args) else: cs.console_control(*control_args)
我了解到eval存在极高安全风险,测试发现移除wrapper后,可执行"sys.exit(0) #"这类代码直接退出程序,但当前wrapper可阻止该操作。假设仅需评估数值表达式,现寻求以下问题的解答:
- 如何利用当前代码发起攻击?
- 有无无类实现的动态(输入+条件)评估替代方案?
问题解答
1. 当前代码的攻击方式
虽然wrapper(比如float)会尝试把用户输入转成数值,但依然存在绕过手段:
- 构造恶意输入绕过wrapper:如果
wrapper是float,输入类似"1).__import__('os').system('rm -rf /')#",当str(userIo)处理时,会保留完整字符串,和condition(比如">0")拼接后变成"1).__import__('os').system('rm -rf /')#>0",eval执行时,注释符#会忽略后面的内容,前面的代码会直接执行系统命令。 - 利用Python内置属性执行代码:输入
"0 or __import__('sys').exit()",拼接condition后eval会触发程序退出,或者调用其他危险内置函数。 - 数值类型的字符串拼接注入:即使
wrapper将输入解析为数值,str(userIo)会把数值转回字符串,若输入能被wrapper解析但字符串结构包含可执行代码逻辑(比如利用Python对象的内置方法),依然能触发恶意操作。
2. 无类实现的动态数值评估替代方案
不需要依赖eval,可以通过以下几种安全方式实现动态数值评估:
方案1:手动解析运算符与目标值
针对数值比较场景,拆分condition中的运算符和目标值,用预定义的比较函数实现:
def safe_num_check(user_val, condition): # 定义支持的运算符及对应比较逻辑 op_handlers = [ ('>=', lambda x, y: x >= y), ('<=', lambda x, y: x <= y), ('>', lambda x, y: x > y), ('<', lambda x, y: x < y), ('==', lambda x, y: x == y), ('!=', lambda x, y: x != y) ] # 遍历匹配运算符 for op_str, func in op_handlers: if op_str in condition: try: target = float(condition.split(op_str)[1].strip()) return func(user_val, target) except ValueError: return False return False # 替换console_control中的eval逻辑 if condition: while True: try: userIo = wrapper(input(string)) if safe_num_check(userIo, condition): return userIo else: return None except ValueError: return None
方案2:AST语法树验证后再执行
使用ast模块解析表达式,先验证语法树节点仅包含数值和合法比较运算符,再执行评估:
import ast def safe_eval_expr(user_val, condition): expr_str = f"{user_val} {condition}" try: tree = ast.parse(expr_str, mode='eval') # 限制允许的AST节点类型 allowed_nodes = (ast.Expression, ast.Compare, ast.Constant) allowed_ops = (ast.Gt, ast.Lt, ast.GtE, ast.LtE, ast.Eq, ast.NotEq) # 遍历检查所有节点 for node in ast.walk(tree): if not isinstance(node, allowed_nodes): return False if isinstance(node, ast.Compare): for op in node.ops: if not isinstance(op, allowed_ops): return False # 验证通过后执行eval,此时无安全风险 return eval(expr_str) except: return False # 替换console_control中的eval逻辑 if condition: while True: try: userIo = wrapper(input(string)) if safe_eval_expr(userIo, condition): return userIo else: return None except ValueError: return None
方案3:预定义条件映射字典
针对程序中常用的比较条件,直接将condition字符串映射为对应的比较函数:
# 预定义常用条件的映射 condition_map = { '> 0': lambda x: x > 0, '<= 1000': lambda x: x <= 1000, '>= 10': lambda x: x >= 10, # 根据需求添加更多条件 } # 在console_control中使用 if condition: while True: try: userIo = wrapper(input(string)) if condition in condition_map and condition_map[condition](userIo): return userIo else: return None except ValueError: return None
内容的提问来源于stack exchange,提问作者Paulo Martins
相关产品推荐
相关产品推荐

