全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-19 10 分钟 ✍️ juanwangdev

Python代码注入防护

代码注入是最危险的安全漏洞之一,可能导致任意代码执行。掌握防护策略至关重要。

eval/exec 注入防护

危险示例

Python
# 绝对不要这样做!
user_input = "__import__('os').system('rm -rf /')"
result = eval(user_input)  # 极危险!

# 常见攻击模式:
# __import__('os').system('恶意命令')
# open('/etc/passwd').read()
# (lambda: ().__class__.__base__.__subclasses__())()[...]

安全替代方案

Python
# 方案1:使用安全的表达式解析器
from ast import parse, literal_eval

# literal_eval 只支持常量表达式
safe_result = literal_eval("[1, 2, 3]")  # 安全
# literal_eval("__import__('os')")       # ValueError

# 方案2:自定义表达式处理器
class SafeCalculator:
    "安全的数学表达式计算"

    ALLOWED_OPERATIONS = {
        '+': lambda a, b: a + b,
        '-': lambda a, b: a - b,
        '*': lambda a, b: a * b,
        '/': lambda a, b: a / b if b != 0 else None,
        'pow': lambda a, b: a ** b,
    }

    def calculate(self, expression):
        import re
        # 只允许数字和运算符
        if not re.match(r'^[\d\s+\-*/.()]+$', expression):
            raise ValueError("非法字符")
        try:
            return eval(expression, {'__builtins__': {}}, {})
        except:
            raise ValueError("计算错误")

calc = SafeCalculator()
print(calc.calculate("2 + 3 * 4"))  # 14

受限执行环境

Python
import ast

class SafeExecutor:
    "受限执行环境"

    SAFE_BUILTINS = {
        'abs': abs, 'min': min, 'max': max,
        'len': len, 'sum': sum, 'range': range,
        'list': list, 'tuple': tuple, 'dict': dict,
        'str': str, 'int': int, 'float': float,
        'bool': bool, 'sorted': sorted,
    }

    FORBIDDEN_NODES = {
        ast.Import, ast.ImportFrom,
        ast.Call, ast.Attribute, ast.Subscript,
    }

    def check_ast(self, code):
        "AST 安全检查"
        tree = ast.parse(code, mode='eval')

        for node in ast.walk(tree):
            if node.__class__ in self.FORBIDDEN_NODES:
                # 允许部分安全的调用
                if isinstance(node, ast.Call):
                    if isinstance(node.func, ast.Name):
                        if node.func.id in self.SAFE_BUILTINS:
                            continue
                raise SecurityError(f"禁止节点: {node.__class__.__name__}")

        return tree

    def safe_eval(self, expression):
        "安全执行表达式"
        tree = self.check_ast(expression)
        return eval(expression, {'__builtins__': self.SAFE_BUILTINS}, {})

executor = SafeExecutor()
print(executor.safe_eval("abs(-5) + max(1, 2)"))  # 7
# executor.safe_eval("__import__('os')")         # SecurityError

模板注入防护

Jinja2 安全配置

Python
from jinja2 import Environment, BaseLoader, select_autoescape

# 安全配置
env = Environment(
    loader=BaseLoader(),
    autoescape=select_autoescape(['html', 'xml']),  # 自动转义
)

# 模板渲染
template = env.from_string("Hello {{ name }}!")
safe_output = template.render(name="<script>alert('xss')</script>")
print(safe_output)  # Hello &lt;script&gt;alert('xss')&lt;/script&gt;!

# 禁用危险功能
sandbox_env = Environment(
    loader=BaseLoader(),
    autoescape=True,
)
# 不暴露危险对象到模板上下文
safe_context = {'name': 'safe', 'items': [1, 2, 3]}

SSTI(服务端模板注入)防护

Python
# 危险模式:模板字符串拼接
dangerous_template = "Hello " + user_input  # 如果 user_input 包含模板语法
# "{{config.items()}}" 可能泄露配置

# 安全方案:
# 1. 使用参数化模板
template = env.from_string("Hello {{ name }}!")
result = template.render(name=user_input)  # user_input 作为数据

# 2. 禁止模板语法
def safe_text(text):
    "移除模板语法"
    import re
    return re.sub(r'\{[{%#].*?[%#}]\}', '', text)

clean_text = safe_text(user_input)
template = env.from_string("Hello " + clean_text)

命令注入防护

subprocess 安全使用

Python
import subprocess

# 危险方式:shell=True
# subprocess.call(f"ls {user_input}", shell=True)  # 危险!

# 安全方式:shell=False,参数列表
result = subprocess.run(
    ['ls', '-l', user_input],  # 参数分离
    shell=False,
    capture_output=True,
    text=True
)

# 验证输入
import re

def safe_filename(filename):
    "验证文件名"
    # 只允许安全字符
    if not re.match(r'^[\w\-\.]+$', filename):
        raise ValueError("非法文件名")
    return filename

safe_input = safe_filename(user_input)
result = subprocess.run(['ls', safe_input], shell=False)
Python
# 安全执行外部命令
class SafeCommand:
    "安全命令执行器"

    ALLOWED_COMMANDS = {'ls', 'cat', 'grep', 'echo'}

    def execute(self, command, args):
        "安全执行"
        if command not in self.ALLOWED_COMMANDS:
            raise ValueError(f"禁止命令: {command}")

        # 参数验证
        safe_args = [self.validate_arg(arg) for arg in args]

        return subprocess.run(
            [command] + safe_args,
            shell=False,
            capture_output=True,
            text=True,
            timeout=10  # 限制执行时间
        )

    def validate_arg(self, arg):
        "验证参数"
        if not re.match(r'^[\w\-\.\/]+$', str(arg)):
            raise ValueError(f"非法参数: {arg}")
        return arg

executor = SafeCommand()
result = executor.execute('ls', ['-l', '/tmp'])

os.system 替代方案

Python
import os

# 危险:os.system
# os.system(f"ls {user_input}")  # 危险!

# 安全替代:
# 1. 使用 subprocess
subprocess.run(['ls', user_input], shell=False)

# 2. 使用 shlex 分割
import shlex
cmd = "ls -l"
parts = shlex.split(cmd)
subprocess.run(parts, shell=False)

SQL 注入防护

Python
import sqlite3

# 危险方式:字符串拼接
# query = f"SELECT * FROM users WHERE name = '{user_input}'"  # 危险!

# 安全方式:参数化查询
conn = sqlite3.connect('database.db')
cursor = conn.cursor()

# 使用占位符
cursor.execute(
    "SELECT * FROM users WHERE name = ?",
    (user_input,)  # 参数作为元组
)

# 多个参数
cursor.execute(
    "SELECT * FROM users WHERE name = ? AND age > ?",
    (user_input, min_age)
)
Python
# SQLAlchemy 安全用法
from sqlalchemy import create_engine, text

engine = create_engine('sqlite:///database.db')

with engine.connect() as conn:
    # 参数化查询
    result = conn.execute(
        text("SELECT * FROM users WHERE name = :name"),
        {'name': user_input}
    )

输入验证函数

Python
import re
from typing import Any

class InputValidator:
    "输入验证器"

    @staticmethod
    def validate_alphanumeric(value: str, max_length: int = 100) -> str:
        "验证字母数字字符串"
        if not isinstance(value, str):
            raise TypeError("期望字符串类型")
        if len(value) > max_length:
            raise ValueError(f"长度超过 {max_length}")
        if not re.match(r'^[a-zA-Z0-9_\-]+$', value):
            raise ValueError("包含非法字符")
        return value

    @staticmethod
    def validate_email(email: str) -> str:
        "验证邮箱格式"
        pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        if not re.match(pattern, email):
            raise ValueError("非法邮箱格式")
        return email

    @staticmethod
    def validate_path(path: str) -> str:
        "验证路径"
        # 防止路径遍历
        if '..' in path or path.startswith('/'):
            raise ValueError("非法路径")
        if not re.match(r'^[\w\-\.\/]+$', path):
            raise ValueError("路径包含非法字符")
        return path

    @staticmethod
    def validate_int(value: Any, min_val: int, max_val: int) -> int:
        "验证整数范围"
        try:
            num = int(value)
        except (TypeError, ValueError):
            raise ValueError("期望整数类型")

        if not min_val <= num <= max_val:
            raise ValueError(f"数值必须在 {min_val}{max_val} 之间")
        return num

validator = InputValidator()
safe_name = validator.validate_alphanumeric(user_input)

整体防护策略

Python
class SecurityMiddleware:
    "安全中间件"

    def __init__(self):
        self.validator = InputValidator()

    def sanitize_input(self, data: dict) -> dict:
        "清理输入数据"
        sanitized = {}
        for key, value in data.items():
            # 根据键名选择验证策略
            if key in ('name', 'username'):
                sanitized[key] = self.validator.validate_alphanumeric(value)
            elif key == 'email':
                sanitized[key] = self.validator.validate_email(value)
            elif key == 'path':
                sanitized[key] = self.validator.validate_path(value)
            elif key in ('id', 'age', 'count'):
                sanitized[key] = self.validator.validate_int(value, 0, 1000000)
            else:
                # 默认:移除危险字符
                sanitized[key] = self.sanitize_default(value)
        return sanitized

    def sanitize_default(self, value: str) -> str:
        "默认清理"
        if isinstance(value, str):
            # 移除潜在危险字符
            return re.sub(r'[<>&\'"\\]', '', value)
        return value

# 使用
middleware = SecurityMiddleware()
safe_data = middleware.sanitize_input({
    'name': 'Alice',
    'email': 'alice@example.com',
    'path': 'safe/path'
})

要点总结

  1. 禁止直接eval/exec用户输入
  2. **literal_eval**仅支持常量表达式,是安全替代
  3. 模板引擎启用自动转义,禁用危险上下文
  4. **subprocess**使用shell=False,参数分离传递
  5. SQL查询使用参数化,禁止字符串拼接

📝 发现内容有误?点击此处直接编辑

← 上一篇 Python SSL/TLS配置
下一篇 → Python加密与解密操作
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库