# 支付安全与合规指南 (Security & Compliance) v4.0 > 支付系统安全最佳实践,保护你的资金和用户数据 ## 🔐 密钥安全 ### 1. 密钥存储原则 ``` ❌ 错误做法: - 将密钥硬编码在代码中 - 将密钥提交到 Git 仓库 - 通过即时通讯工具传输密钥 - 使用弱密码作为 API Key ✅ 正确做法: - 使用环境变量存储密钥 - 使用专业密钥管理服务 (AWS KMS, HashiCorp Vault) - 定期轮换密钥 - 最小权限原则 ``` ### 2. .gitignore 必须包含 ```gitignore # 支付密钥相关 .env .env.local .env.*.local *.pem *.key cert/ config/payment.yml secrets/ ``` ### 3. 密钥轮换 - 定期更换 API 密钥 (建议每 90 天) - 发现泄露立即作废并重新生成 - 保留旧密钥短暂过渡期 --- ## 🔒 通信安全 ### 1. HTTPS 强制 ```nginx # Nginx 配置示例 server { listen 80; server_name your-domain.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name your-domain.com; ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; # HSTS add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; } ``` ### 2. 证书管理 - 使用受信任的 CA 签发证书 - 定期检查证书有效期 - 推荐 Let's Encrypt 自动续期 --- ## ✅ 签名验证 ### 1. 支付宝签名验证 ```python from Crypto.PublicKey import RSA from Crypto.Signature import PKCS1_v1_5 from Crypto.Hash import SHA256 import base64 def verify_alipay_sign(params: dict, sign: str, public_key: str) -> bool: """验证支付宝签名""" # 1. 参数排序 sorted_params = sorted([(k, v) for k, v in params.items() if k != 'sign' and v]) # 2. 拼接待签名字符串 sign_str = '&'.join([f'{k}={v}' for k, v in sorted_params]) # 3. RSA2 验签 key = RSA.import_key(f"-----BEGIN PUBLIC KEY-----\n{public_key}\n-----END PUBLIC KEY-----") verifier = PKCS1_v1_5.new(key) hash_obj = SHA256.new(sign_str.encode('utf-8')) try: verifier.verify(hash_obj, base64.b64decode(sign)) return True except (ValueError, TypeError): return False ``` ### 2. 微信签名验证 ```python import hashlib def verify_wechat_sign(params: dict, sign: str, api_key: str) -> bool: """验证微信支付签名""" # 1. 参数排序 sorted_params = sorted([(k, v) for k, v in params.items() if k != 'sign' and v]) # 2. 拼接待签名字符串 sign_str = '&'.join([f'{k}={v}' for k, v in sorted_params]) sign_str += f'&key={api_key}' # 3. MD5 签名 calculated_sign = hashlib.md5(sign_str.encode('utf-8')).hexdigest().upper() return calculated_sign == sign ``` --- ## 💰 金额校验 ### 1. 回调金额必须验证 ```python def handle_payment_notify(trade_sn: str, paid_amount: int): """处理支付回调时必须验证金额""" trade = get_trade_by_sn(trade_sn) # 金额必须严格匹配 if paid_amount != trade.cash_amount: log.error(f"金额不匹配! 订单:{trade.cash_amount}, 回调:{paid_amount}") raise AmountMismatchError() # 继续处理... ``` ### 2. 防止金额篡改 ```python # 前端传入的金额仅用于展示,实际金额从后端订单读取 def checkout(order_sn: str, gateway: str): order = get_order(order_sn) # 金额从数据库读取,不信任前端 amount = order.pay_amount return create_trade(order_sn, amount, gateway) ``` --- ## 🛡️ 回调安全 ### 1. IP 白名单 ```python # 支付平台回调 IP 白名单 PAYMENT_IP_WHITELIST = { 'alipay': [ '110.75.0.0/16', '203.209.0.0/16' ], 'wechat': [ '101.226.0.0/16', '140.207.0.0/16' ] } def verify_callback_ip(gateway: str, client_ip: str) -> bool: """验证回调来源 IP""" import ipaddress whitelist = PAYMENT_IP_WHITELIST.get(gateway, []) client = ipaddress.ip_address(client_ip) for cidr in whitelist: if client in ipaddress.ip_network(cidr): return True return False ``` ### 2. 防重放攻击 ```python import time def check_notify_timestamp(timestamp: int) -> bool: """检查回调时间戳,防止重放攻击""" now = int(time.time()) # 允许 5 分钟的时间差 if abs(now - timestamp) > 300: log.warning(f"回调时间戳异常: {timestamp}") return False return True ``` ### 3. 幂等性处理 ```python def process_notify_idempotent(trade_sn: str, notify_data: dict): """幂等性处理回调""" # 使用分布式锁 lock_key = f"payment_notify:{trade_sn}" with redis_lock(lock_key, timeout=10): trade = get_trade_by_sn(trade_sn) # 已处理过,直接返回成功 if trade.status == 'paid': return success_response() # 处理支付成功逻辑 update_trade_to_paid(trade, notify_data) return success_response() ``` --- ## 📝 日志审计 ### 1. 必须记录的日志 ```python import logging payment_logger = logging.getLogger('payment') # 创建交易日志 payment_logger.info(f"创建交易 | trade_sn={trade_sn} | order_sn={order_sn} | amount={amount} | gateway={gateway}") # 回调日志 payment_logger.info(f"收到回调 | gateway={gateway} | trade_sn={trade_sn} | raw_data={raw_data[:500]}") # 签名验证日志 payment_logger.info(f"签名验证 | trade_sn={trade_sn} | result={verify_result}") # 状态变更日志 payment_logger.info(f"状态变更 | trade_sn={trade_sn} | from={old_status} | to={new_status}") # 退款日志 payment_logger.info(f"发起退款 | refund_sn={refund_sn} | trade_sn={trade_sn} | amount={amount}") ``` ### 2. 敏感信息脱敏 ```python def mask_sensitive(data: dict) -> dict: """敏感信息脱敏""" sensitive_keys = ['card_no', 'id_card', 'phone', 'bank_account'] masked = data.copy() for key in sensitive_keys: if key in masked: value = str(masked[key]) if len(value) > 4: masked[key] = value[:2] + '*' * (len(value) - 4) + value[-2:] return masked ``` --- ## 🚨 异常处理 ### 1. 支付异常分类 ```python class PaymentError(Exception): """支付基础异常""" pass class SignatureError(PaymentError): """签名验证失败""" pass class AmountMismatchError(PaymentError): """金额不匹配""" pass class OrderExpiredError(PaymentError): """订单已过期""" pass class DuplicatePaymentError(PaymentError): """重复支付""" pass class RefundError(PaymentError): """退款失败""" pass ``` ### 2. 统一异常处理 ```python @app.exception_handler(PaymentError) async def payment_exception_handler(request, exc): return JSONResponse( status_code=400, content={ "code": 400, "message": str(exc), "data": None } ) ``` --- ## ✅ 合规要求 ### 1. PCI DSS 合规 (信用卡) - 不存储完整卡号、CVV、PIN - 使用 Stripe/PayPal 等符合 PCI DSS 的支付网关 - 定期安全评估 ### 2. GDPR 合规 (欧盟用户) - 明确告知用户数据用途 - 提供数据删除功能 - 用户同意授权 ### 3. 中国支付合规 - 接入持牌支付机构 - 实名认证 - 交易限额管理 --- ## 📋 安全检查清单 ```markdown ## 上线前安全检查 ### 密钥管理 - [ ] 所有密钥通过环境变量配置 - [ ] 密钥未提交到代码仓库 - [ ] 生产环境密钥与测试环境隔离 ### 通信安全 - [ ] 启用 HTTPS - [ ] 证书有效且受信任 - [ ] 启用 HSTS ### 签名验证 - [ ] 所有回调验签 - [ ] 验签失败拒绝处理 ### 金额校验 - [ ] 回调金额与订单金额比对 - [ ] 金额从后端读取 ### 日志审计 - [ ] 关键操作有日志 - [ ] 敏感信息脱敏 ### 异常处理 - [ ] 异常不泄露敏感信息 - [ ] 有统一异常处理 ### 回调安全 - [ ] IP 白名单验证 (可选) - [ ] 幂等性处理 - [ ] 防重放攻击 ```