跳到主要内容

V2 安全升级计划

分支: v2-security-upgrade | 目标: 平衡开发便捷性与生产安全性


执行原则

环境分级策略

环境安全级别便捷性特点
development允许 auth_disabled, 宽松 CORS
staging强制认证, 允许测试域名 CORS
production严格 CORS, 私钥加密, 完整审计

核心设计

┌─────────────────────────────────────────────────────────────────┐
│ 环境感知安全配置 │
│ if environment == "development" → 便捷模式 │
│ if environment == "staging" → 平衡模式 │
│ if environment == "production" → 安全模式 │
└─────────────────────────────────────────────────────────────────┘

Phase 1: 安全基础设施 (P0)

1.1 CORS 环境感知配置

fn create_cors_layer(config: &AppConfig) -> CorsLayer {
match config.environment.as_str() {
"development" => {
// 开发环境: 允许所有来源
CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any)
}
"staging" => {
// 测试环境: 允许测试域名
CorsLayer::new()
.allow_origin([
"https://8a27.xyz".parse().unwrap(),
"http://localhost:3000".parse().unwrap(),
])
.allow_credentials(true)
}
_ => {
// 生产环境: 仅允许正式域名
CorsLayer::new()
.allow_origin([
"https://renance.xyz".parse().unwrap(),
"https://app.renance.xyz".parse().unwrap(),
])
.allow_credentials(true)
}
}
}

1.2 认证绕过环境保护

if state.config.is_auth_disabled() {
// 安全检查: 仅 development 环境允许禁用认证
if state.config.environment != "development" {
tracing::error!(
"SECURITY: AUTH_DISABLED=true in {} environment! Ignoring.",
state.config.environment
);
// 不绕过认证,继续正常验证流程
} else {
tracing::warn!("Auth disabled in development mode");
// 允许绕过
}
}

1.3 认证错误信息脱敏

  • 开发环境: 显示详细错误信息
  • 生产环境: 返回统一的 "Authentication failed"

Phase 2: 数据完整性 (P1)

2.1 数据库事务支持

pub async fn persist_trade(pool: &PgPool, trade: &TradeEvent) -> Result<(), MatchingError> {
let mut tx = pool.begin().await?;

// 1. 插入交易记录
sqlx::query("INSERT INTO trades ...").execute(&mut *tx).await?;

// 2. 更新 Maker 仓位
Self::update_position(&mut tx, &trade.maker).await?;

// 3. 更新 Taker 仓位
Self::update_position(&mut tx, &trade.taker).await?;

// 4. 提交事务
tx.commit().await?;

Ok(())
}

2.2 重试机制

pub struct RetryConfig {
pub max_attempts: u32, // 默认 3
pub initial_delay_ms: u64, // 默认 100ms
pub max_delay_ms: u64, // 默认 5000ms
pub backoff_multiplier: f64, // 默认 2.0
}

2.3 死信队列

失败超过重试次数的交易写入 dead_letter_trades 表,便于后续人工处理。


Phase 3: 性能优化 (P1)

3.1 修复 N+1 查询

优化前: 每个仓位单独查询价格 优化后: 批量获取所有需要的价格

// 批量获取所有需要的价格 (一次查询)
let symbols: Vec<String> = positions.iter().map(|p| p.symbol.clone()).collect();
let prices = state.price_feed_service
.batch_get_mark_prices(&symbols)
.await
.unwrap_or_default();

Phase 4: 常量提取 (P2)

全局常量模块

// backend/src/constants.rs

/// 缓存 TTL (秒)
pub mod cache_ttl {
pub const PRICE: u64 = 5;
pub const TICKER: u64 = 5;
pub const BALANCE: u64 = 30;
pub const POSITIONS: u64 = 10;
pub const SESSION: u64 = 86400; // 24 hours
}

/// USDT 精度
pub mod precision {
pub const USDT_DECIMALS: u8 = 6;
pub const USDT_MULTIPLIER: u64 = 1_000_000;
}

/// 通道容量
pub mod channels {
pub const TRADE_CHANNEL_CAPACITY: usize = 10000;
pub const ORDERBOOK_CHANNEL_CAPACITY: usize = 10000;
}

Phase 5: 启动验证 (P2)

配置验证器

impl ConfigValidator {
pub fn validate(config: &AppConfig) -> Result<()> {
let mut errors = Vec::new();

// 必填项检查
if config.jwt_secret.len() < 32 {
errors.push("JWT_SECRET must be at least 32 characters");
}

// 生产环境特殊检查
if config.environment == "production" {
if config.auth_disabled {
errors.push("AUTH_DISABLED cannot be true in production");
}

// 检查是否使用了 Hardhat 测试私钥
if config.backend_signer_private_key.contains("ac0974bec") {
errors.push("Production cannot use Hardhat test private key!");
}
}

if !errors.is_empty() {
bail!("Configuration errors:\n - {}", errors.join("\n - "));
}

Ok(())
}
}

执行时间线

周次阶段内容
Week 1Phase 1CORS 环境感知、认证保护、错误脱敏
Week 2Phase 2数据库事务、重试机制、死信队列
Week 3Phase 3-4N+1 查询修复、常量模块
Week 4Phase 5-6配置验证、main.rs 拆分

测试策略

本地测试

# 开发环境
ENVIRONMENT=development AUTH_DISABLED=true cargo run

# 模拟生产环境
ENVIRONMENT=production AUTH_DISABLED=false cargo run
# 应该: 拒绝启动如果有配置问题

测试服务器 (8a27.xyz)

ENVIRONMENT=staging
CORS_ALLOWED_ORIGINS=https://8a27.xyz,http://localhost:3000

生产环境 (renance.xyz)

ENVIRONMENT=production
CORS_ALLOWED_ORIGINS=https://renance.xyz
# AUTH_DISABLED 无效,强制认证

回滚计划

如果出现问题:

  1. git checkout dev 回到稳定分支
  2. 重新部署 dev 分支代码
  3. 分析问题后在 v2 分支修复