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

自定义数学函数输入场景下替代eval()的安全方案评估与优化问询

Is this custom math function input handling secure? What are ways to improve it?

I want to let users input custom math functions to customize my program's workflow. At first, I used eval() like this:

import numpy as np
x = linspace(0, np.pi)
y = eval(input('y(x)='))
# Process the custom y(x)...

But obviously, eval() has serious security risks—for example, a malicious user could input something like eval("__import__('os').system('clear')", {}) to run arbitrary commands, which is not what I want.

So I designed this processing flow after some research:

import numpy as np
x = np.linspace(0, 2*np.pi)
y = None
user_input = str(input('y(x)='))
# Filter dangerous strings
forbidden_strings = ['import', '\n', '.py', '.exe', '.npz', '.npy', 'load', 'exec', 'compile', ';']
for forbidden_string in forbidden_strings:
    if forbidden_string in user_input:
        raise ValueError("FORBIDDEN COMMAND IN INPUT !")
# Set allowed numpy functions list
allowed_functions = 'mean, max, min, sum, pi, cos, sin, tan, arccos, arcsin, arctan, log, exp, sqrt, power, gradient'
code = "from numpy import " + allowed_functions +"\ny=" + user_input
# The generated code looks like:
# from numpy import all allowed functions
# y= user input
exec(compile(code, filename="", mode="exec"))

Is this solution secure enough? Are there any ways to further enhance its security? Thanks for your help!


Great question—your current approach is a solid step up from raw eval(), but it still has critical gaps that malicious users could exploit. Let’s break this down clearly:

Why your current solution isn’t fully secure

  • Fragile string filtering: Attackers can easily bypass your forbidden list with tricks like uppercase letters (IMPORT), Unicode homoglyphs, or string concatenation ('imp'+'ort'). Your filter won’t catch any of these workarounds.
  • Hidden runtime access: Even with your allowed imports, users can dig into Python’s internal structures. For example, inputting cos.__class__.__base__.__subclasses__() lets them enumerate and access dangerous built-in classes that could lead to arbitrary code execution.
  • Resource exhaustion risks: Users could input expressions that crash or hang your program, like power(10, 10000000) which creates an enormous number, or nested recursive calls that eat up memory.

Actionable ways to harden security

Here are practical, robust improvements you can implement:

  1. Use AST validation instead of string filtering
    Python’s built-in ast module lets you parse the user’s input into an abstract syntax tree, then validate every single node to ensure it only uses safe, allowed constructs. This is infinitely more reliable than blacklisting strings.
    Example implementation:
import ast
import numpy as np

x = np.linspace(0, 2*np.pi)
# Define allowed AST node types (math operations, constants, function calls)
allowed_nodes = {
    ast.Name, ast.Constant, ast.BinOp, ast.UnaryOp,
    ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Pow,
    ast.Neg, ast.Pos, ast.Call
}
# Whitelist of safe numpy functions/values
allowed_funcs = {'cos', 'sin', 'tan', 'pi', 'exp', 'sqrt', 'log', 'mean', 'max'}

def validate_expression(node):
    if type(node) not in allowed_nodes:
        raise ValueError(f"Invalid expression part: {type(node).__name__}")
    # Check function calls are only to allowed functions
    if isinstance(node, ast.Call):
        if not isinstance(node.func, ast.Name) or node.func.id not in allowed_funcs:
            raise ValueError(f"Function '{node.func.id}' is not allowed")
        # Validate all arguments to the function
        for arg in node.args:
            validate_expression(arg)
    # Recursively validate child nodes
    for child in ast.iter_child_nodes(node):
        validate_expression(child)

user_input = input('y(x)=')
try:
    # Parse input as an evaluatable expression
    tree = ast.parse(user_input, mode='eval')
    validate_expression(tree.body)
    # Execute with a strictly restricted scope
    safe_globals = {k: getattr(np, k) for k in allowed_funcs}
    safe_globals['x'] = x
    y = eval(compile(tree, filename='', mode='eval'), safe_globals, {})
except (SyntaxError, ValueError) as e:
    print(f"Invalid input: {e}")

This ensures only explicitly allowed operations and functions are used—no bypasses possible.

  1. Lock down the execution scope
    Never use the default global scope with exec or eval (which includes full access to __builtins__). Instead, pass a minimal dictionary containing only the allowed functions and variables (like the safe_globals in the example above). This eliminates access to dangerous built-ins like open or __import__.

  2. Ditch blacklists entirely
    Blacklist-based filtering is a losing battle—there are endless ways to work around them. Always use a whitelist approach (like AST validation) where you only permit known-safe constructs.

  3. Add resource limits
    Even safe-looking math can crash your program. Use tools like the resource module (on Unix) or win32api (on Windows) to set CPU time and memory limits for evaluating the input. Alternatively, run the evaluation in a separate process that you can terminate if it hangs or uses too many resources.

  4. Test edge cases aggressively
    Try inputting these test cases to see if your implementation catches them:

  • cos.__globals__['__builtins__']['eval']("__import__('os').system('echo hacked')")
  • (lambda x: __import__('os'))()
  • 10**100000000
    If your current code doesn’t block these, it’s a clear sign you need to tighten security.

Final takeaway

Your current approach is better than raw eval(), but it’s not fully secure. The most reliable method is combining AST validation with strict scope restriction—this eliminates nearly all attack surfaces and gives you full control over what users can do.


内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 03:44:10