Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Python

Python eval(): ประเมินนิพจน์จากสตริง - ความสามารถและความเสี่ยง

eval() เป็น Built-in Function ที่ Parse และ Execute สตริงเป็น Python Code ทำให้สามารถสร้าง Dynamic Code ได้ แต่มี Security Risk ร้ายแรง หากใช้กับ Input ที่ไม่ไว้ใจได้

ไวยากรณ์

eval(expression, globals=None, locals=None)
  • expression: สตริงหรือ Code Object ที่จะประเมิน
  • globals: Dictionary ของ Global Variables (Optional)
  • locals: Dictionary ของ Local Variables (Optional)

ตัวอย่างพื้นฐาน

# คำนวณนิพจน์คณิตศาสตร์
expr = "2 + 3 * 4"
print(eval(expr))  # 14

# ใช้ Variable
x = 10
print(eval("x * 2 + 5"))  # 25

# เรียกใช้ Function
print(eval("len('hello')"))  # 5
print(eval("max([1,5,3])"))  # 5

ตัวอย่าง Interactive Calculator

def safe_calculator():
    """Simple Calculator using eval (สำหรับ Demo เท่านั้น - ไม่ปลอดภัยสำหรับ Production)"""
    print("Simple Calculator (type 'quit' to exit)")
    while True:
        try:
            expr = input("Enter expression: ").strip()
            if expr.lower() == 'quit':
                break
            result = eval(expr)
            print(f"Result: {result}")
        except Exception as e:
            print(f"Error: {e}")

# safe_calculator()  # Uncomment to run

⚠️ Security Risks (อันตรายมาก)

eval() รันโค้ดใดๆ ที่ส่งเข้ามา รวมถึง System Commands:

# อันตราย! หาก User Input นี้:
# "__import__('os').system('rm -rf /')"
# "__import__('os').system('cat /etc/passwd')"
# "open('secret.txt').read()"

# eval() จะ Execute จริงๆ!

วิธีป้องกัน (Mitigation Strategies)

1. Restrict Globals และ Locals (ขั้นต่ำ)

# ไม่ให้เข้าถึง Built-ins และ Globals
safe_globals = {"__builtins__": None}
safe_locals = {}

try:
    eval("__import__('os').system('ls')", safe_globals, safe_locals)
except NameError as e:
    print(f"Blocked: {e}")  # Blocked: name '__import__' is not defined

2. Allowlist: อนุญาตเฉพาะ Functions ที่ปลอดภัย

import math

# อนุญาตเฉพาะ Math Functions
safe_dict = {
    "__builtins__": None,
    "abs": abs, "round": round, "min": min, "max": max, "sum": sum,
    "pow": pow, "divmod": divmod,
    # Math module functions
    "sin": math.sin, "cos": math.cos, "tan": math.tan,
    "sqrt": math.sqrt, "log": math.log, "exp": math.exp,
    "pi": math.pi, "e": math.e,
}

# ทดสอบ
print(eval("sqrt(16) + sin(pi/2)", safe_dict))  # 4.0 + 1.0 = 5.0

# พยายามเข้าถึงอันตราย
try:
    eval("__import__('os').system('ls')", safe_dict)
except NameError:
    print("Blocked dangerous call")

3. ใช้ ast.literal_eval แทน (สำหรับ Literals เท่านั้น)

import ast

# ปลอดภัย: Parse เฉพาะ Literals (string, number, tuple, list, dict, bool, None)
print(ast.literal_eval("[1, 2, 3]"))        # [1, 2, 3]
print(ast.literal_eval("{'a': 1}"))         # {'a': 1}
print(ast.literal_eval("'hello'"))          # 'hello'
print(ast.literal_eval("1 + 2"))            # ValueError: ไม่ใช่ Literal

# ไม่สามารถ Execute Code ได้
try:
    ast.literal_eval("__import__('os').system('ls')")
except ValueError as e:
    print(f"Safe: {e}")

4. ใช้ Library เฉพาะทาง: simpleeval, asteval

# pip install simpleeval
from simpleeval import simple_eval

# Safe Expression Evaluator
print(simple_eval("2 + 3 * 4"))           # 14
print(simple_eval("sqrt(16)"))            # Error: name 'sqrt' not defined

# เพิ่ม Functions
print(simple_eval("sqrt(16)", functions={"sqrt": lambda x: x**0.5}))  # 4.0

# Blocked dangerous operations
try:
    simple_eval("__import__('os').system('ls')")
except Exception as e:
    print(f"Blocked: {type(e).__name__}")

เมื่อไหร่ควร/ไม่ควรใช้ eval()

สถานการณ์ควรใช้ eval()?ทางเลือก
User Input จาก Web Form/API❌ ห้ามเด็ดขาดast.literal_eval, simpleeval, JSON
Config File ที่คุณควบคุมได้เต็มที่⚠️ ยังเสี่ยงYAML, JSON, TOML, ConfigParser
Internal Tool / Trusted Developer Only⚠️ ระวัง (ยังมี Bug Risk)ast.literal_eval, Custom Parser
Math Expression Parser (Calculator App)⚠️ ถ้าจำเป็นsimpleeval, asteval, numexpr
Dynamic Code Generation (Plugin System)⚠️ Advanced OnlyImportlib, Entry Points, WASM

สรุป

  • eval() มีพลังมากแต่อันตราย - อย่าใช้กับ Untrusted Input
  • ใช้ ast.literal_eval() สำหรับ Parse Literals (Safe)
  • ใช้ simpleeval หรือ asteval สำหรับ Expression Evaluation (Safer)
  • ถ้าจำเป็นต้องใช้ eval(): จำกัด __builtins__=None และ Allowlist Functions เท่านั้น
  • Audit Code ที่ใช้ eval() อย่างสม่ำเสมอ