Python接口安全案例如何加固接口安全

wen python案例 28

本文目录导读:

Python接口安全案例如何加固接口安全

  1. 身份认证层加固
  2. 请求验证与过滤
  3. 通信安全
  4. 限流与防护
  5. 安全日志与监控
  6. 敏感信息保护
  7. 依赖安全
  8. 实施建议

我来从多个维度详细介绍Python接口安全加固的实战方案。

身份认证层加固

JWT令牌认证

import jwt
from datetime import datetime, timedelta
from functools import wraps
from flask import request, jsonify
SECRET_KEY = "your-secret-key-here"
def generate_token(user_id, role):
    payload = {
        'user_id': user_id,
        'role': role,
        'exp': datetime.utcnow() + timedelta(hours=2),  # 短有效期
        'iat': datetime.utcnow(),
        'jti': str(uuid.uuid4())  # 唯一标识,防止重放
    }
    return jwt.encode(payload, SECRET_KEY, algorithm='HS256')
def token_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization')
        if not token or not token.startswith('Bearer '):
            return jsonify({'error': 'Missing or invalid token'}), 401
        try:
            token = token.split(' ')[1]
            payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
            # 检查token是否被撤销(需要在Redis中维护黑名单)
            if check_token_revoked(payload['jti']):
                return jsonify({'error': 'Token revoked'}), 401
            request.current_user = payload
        except jwt.ExpiredSignatureError:
            return jsonify({'error': 'Token expired'}), 401
        except jwt.InvalidTokenError:
            return jsonify({'error': 'Invalid token'}), 401
        return f(*args, **kwargs)
    return decorated
@app.route('/api/secure/data', methods=['GET'])
@token_required
def get_secure_data():
    return jsonify({'data': '敏感数据'})

基于角色的访问控制(RBAC)

def role_required(allowed_roles):
    def decorator(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            if request.current_user.get('role') not in allowed_roles:
                return jsonify({'error': 'Insufficient permissions'}), 403
            return f(*args, **kwargs)
        return decorated
    return decorator
@app.route('/api/admin/users', methods=['GET'])
@token_required
@role_required(['admin', 'superadmin'])
def get_users():
    return jsonify({'users': []})

请求验证与过滤

输入验证

from pydantic import BaseModel, Field, validator
from flask_pydantic import validate
class UserCreateRequest(BaseModel):
    username: str = Field(..., min_length=3, max_length=50, regex='^[a-zA-Z0-9_]+$')
    email: str = Field(..., regex='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
    age: int = Field(..., ge=0, le=150)
    @validator('username')
    def username_must_be_valid(cls, v):
        if v.lower() in ['admin', 'root', 'system']:
            raise ValueError('Username not allowed')
        return v
@app.route('/api/users', methods=['POST'])
@validate(body=UserCreateRequest)
def create_user():
    data = request.body_params
    # 安全的创建用户逻辑
    return jsonify({'message': 'User created'}), 201

SQL注入防护

from sqlalchemy import text
from sqlalchemy.exc import SQLAlchemyError
# 错误示例 - 不要这样做
@app.route('/api/users/search')
def search_users_vulnerable():
    keyword = request.args.get('q', '')
    # 危险:直接拼接SQL
    query = f"SELECT * FROM users WHERE name LIKE '%{keyword}%'"
    result = db.session.execute(query)
    return jsonify({'results': [r.to_dict() for r in result]})
# 正确示例 - 参数化查询
@app.route('/api/users/search')
def search_users_secure():
    keyword = request.args.get('q', '')
    # 安全:使用参数化查询
    query = text("SELECT * FROM users WHERE name LIKE :keyword")
    result = db.session.execute(query, {'keyword': f'%{keyword}%'})
    return jsonify({'results': [r.to_dict() for r in result]})

通信安全

HTTPS强制和HSTS

from flask_talisman import Talisman
app = Flask(__name__)
# 配置安全头
csp = {
    'default-src': "'self'",
    'script-src': "'self' 'unsafe-inline'",
    'style-src': "'self' 'unsafe-inline'",
}
talisman = Talisman(
    app,
    force_https=True,  # 强制HTTPS
    strict_transport_security=True,  # HSTS
    strict_transport_security_preload=True,
    session_cookie_secure=True,
    content_security_policy=csp,
    feature_policy="camera 'none'; microphone 'none'"
)

API请求加密

from cryptography.fernet import Fernet
import base64
class RequestEncryption:
    def __init__(self):
        # 生产环境应从密钥管理系统获取
        self.key = Fernet.generate_key()
        self.cipher = Fernet(self.key)
    def encrypt_payload(self, data: dict) -> str:
        json_str = json.dumps(data)
        encrypted = self.cipher.encrypt(json_str.encode())
        return base64.urlsafe_b64encode(encrypted).decode()
    def decrypt_payload(self, encrypted_data: str) -> dict:
        try:
            encrypted = base64.urlsafe_b64decode(encrypted_data)
            decrypted = self.cipher.decrypt(encrypted)
            return json.loads(decrypted.decode())
        except Exception as e:
            raise ValueError("解密失败")
@app.route('/api/secure/transfer', methods=['POST'])
def secure_transfer():
    encrypted_data = request.json.get('encrypted_data')
    try:
        decrypted = RequestEncryption().decrypt_payload(encrypted_data)
        # 处理解密后的数据
        return jsonify({'status': 'success'})
    except ValueError:
        return jsonify({'error': 'Invalid encrypted data'}), 400

限流与防护

API速率限制

from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(
    app,
    key_func=get_remote_address,
    default_limits=["200 per day", "50 per hour"]
)
# 针对不同端点设置不同限制
@app.route("/api/login")
@limiter.limit("5 per minute", override_defaults=False)
def login():
    return "Login endpoint"
@app.route("/api/search")
@limiter.limit("30 per minute")
def search():
    return "Search endpoint"

请求签名验证

import hmac
import hashlib
def verify_request_signature(request_data, signature, secret_key):
    """HMAC签名验证"""
    # 将请求参数按字母序排列
    sorted_params = sorted(request_data.items())
    message = '&'.join([f"{k}={v}" for k, v in sorted_params])
    # 计算签名
    expected_signature = hmac.new(
        secret_key.encode(),
        message.encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected_signature)
@app.route('/api/webhook', methods=['POST'])
def webhook_handler():
    signature = request.headers.get('X-Signature')
    if not verify_request_signature(request.json, signature, WEBHOOK_SECRET):
        return jsonify({'error': 'Invalid signature'}), 401
    # 处理webhook
    return jsonify({'status': 'ok'})

安全日志与监控

import logging
from flask import g, request
import json
# 配置安全审计日志
audit_logger = logging.getLogger('audit')
audit_logger.setLevel(logging.INFO)
handler = logging.FileHandler('audit.log')
handler.setFormatter(logging.Formatter(
    '%(asctime)s - %(levelname)s - %(message)s'
))
audit_logger.addHandler(handler)
@app.before_request
def log_request():
    g.start_time = time.time()
    # 记录敏感操作
    if request.method in ['POST', 'PUT', 'DELETE']:
        audit_logger.info(json.dumps({
            'ip': request.remote_addr,
            'method': request.method,
            'path': request.path,
            'user_agent': request.user_agent.string,
            'timestamp': datetime.utcnow().isoformat()
        }))
@app.after_request
def log_response(response):
    duration = time.time() - g.start_time
    # 记录错误响应
    if response.status_code >= 400:
        audit_logger.warning(json.dumps({
            'ip': request.remote_addr,
            'path': request.path,
            'status_code': response.status_code,
            'duration': duration
        }))
    return response

敏感信息保护

import re
class SensitiveDataFilter:
    @staticmethod
    def mask_sensitive_data(data: dict) -> dict:
        """脱敏敏感数据"""
        masked = data.copy()
        if 'password' in masked:
            masked['password'] = '********'
        if 'credit_card' in masked:
            # 保留后4位
            card = masked['credit_card']
            masked['credit_card'] = f"****-****-****-{card[-4:]}"
        if 'email' in masked:
            email = masked['email']
            parts = email.split('@')
            masked['email'] = f"{parts[0][:2]}***@{parts[1]}"
        if 'phone' in masked:
            phone = masked['phone']
            masked['phone'] = f"{phone[:3]}****{phone[-4:]}"
        return masked
# 响应数据脱敏
@app.after_request
def filter_sensitive_data(response):
    if response.is_json and response.status_code == 200:
        data = response.get_json()
        if isinstance(data, dict):
            filtered = SensitiveDataFilter.mask_sensitive_data(data)
            response.set_data(json.dumps(filtered))
    return response

依赖安全

# requirements.txt 安全最佳实践
"""
# 固定版本号
Flask==2.3.2
SQLAlchemy==2.0.19
PyJWT==2.8.0
# 安全扫描工具
safety==2.3.5
bandit==1.7.5
"""
# 安全扫描脚本
import subprocess
def security_check():
    """运行安全检查"""
    print("Running safety check...")
    subprocess.run(["safety", "check", "--full-report"])
    print("Running bandit check...")
    subprocess.run(["bandit", "-r", "."])
    print("Checking for vulnerable dependencies...")
    subprocess.run(["pip-audit"])

实施建议

  1. 分层防护:不要依赖单一的安全机制
  2. 最小权限:只授予必要的权限
  3. 持续监控:实时监控异常行为
  4. 定期审计:定期审查安全日志和代码
  5. 及时更新:保持依赖库和框架的最新版本
  6. 安全测试:定期进行渗透测试和安全评估

方案需要根据实际业务场景和合规要求进行调整。

抱歉,评论功能暂时关闭!