You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

控制台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. 如何利用当前代码发起攻击?
  2. 有无无类实现的动态(输入+条件)评估替代方案?

问题解答

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 22:45:33