SECURITY.md 14 KB

🔒 安全保障体系

架构安全设计

1. 多层隔离架构

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   前端UI层      │    │   Rust核心层    │    │   系统层         │
│   (Vue3+TS)     │◄──►│   (Tauri后端)   │◄──►│   (OS API)      │
│                 │    │                 │    │                 │
│ ✅ 无敏感逻辑   │    │ ✅ 核心业务逻辑 │    │ ✅ 受限系统访问 │
│ ✅ UI展示       │    │ ✅ 数据加密     │    │ ✅ 沙箱环境     │
│ ✅ 用户交互     │    │ ✅ 安全验证     │    │ ✅ 权限控制     │
└─────────────────┘    └─────────────────┘    └─────────────────┘

2. Rust安全性优势

内存安全

// Rust编译器保证内存安全
fn safe_memory_handling(data: &str) -> Result<String, Box<dyn std::error::Error>> {
    // 不可能出现缓冲区溢出、空指针解引用等内存安全问题
    Ok(data.to_string())
}

所有权系统

// 编译时检查资源所有权,防止数据竞争
fn secure_data_handling(mut data: Vec<u8>) -> Vec<u8> {
    // data的所有权在函数内部,不会发生并发访问冲突
    data
}

类型安全

// 强类型系统防止类型混淆攻击
#[derive(Debug, Serialize, Deserialize)]
struct SecureAccount {
    username: String,
    encrypted_password: Vec<u8>,
    session_token: Option<String>,
}

数据安全保护

1. 加密存储体系

AES-256加密存储

use aes_gcm::{Aes256Gcm, Key, Nonce};
use aes_gcm::aead::{Aead, KeyInit};

fn encrypt_sensitive_data(data: &[u8], key: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    let cipher = Aes256Gcm::new(Key::<Aes256Gcm>::from_slice(key));
    let nonce = Nonce::from_slice(b"unique nonce"); // 实际使用随机nonce

    let ciphertext = cipher.encrypt(nonce, data)
        .map_err(|e| format!("Encryption failed: {}", e))?;

    Ok(ciphertext)
}

PBKDF2密钥派生

use pbkdf2::pbkdf2_hmac;
use sha2::Sha256;

fn derive_key(password: &str, salt: &[u8]) -> [u8; 32] {
    let mut key = [0u8; 32];
    pbkdf2_hmac::<Sha256>(password.as_bytes(), salt, 100_000, &mut key);
    key
}

2. 敏感数据处理

内存中的数据保护

use zeroize::Zeroize;

struct SensitiveData {
    api_key: String,
    session_token: Vec<u8>,
}

impl Drop for SensitiveData {
    fn drop(&mut self) {
        self.api_key.zeroize();
        self.session_token.zeroize();
    }
}

安全的随机数生成

use ring::rand::{SecureRandom, SystemRandom};

fn generate_secure_random_bytes(length: usize) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    let rng = SystemRandom::new();
    let mut bytes = vec![0u8; length];

    rng.fill(&mut bytes)
        .map_err(|_| "Failed to generate random bytes".into())?;

    Ok(bytes)
}

网络安全保护

1. HTTPS强制使用

use reqwest::Client;

async fn secure_http_request(url: &str) -> Result<String, Box<dyn std::error::Error>> {
    let client = Client::builder()
        .https_only(true)  // 强制HTTPS
        .timeout(std::time::Duration::from_secs(30))
        .build()?;

    let response = client.get(url).send().await?;
    let cert = response.extensions().get::<reqwest::tls::Certificate>();

    // 验证证书指纹
    if let Some(cert) = cert {
        verify_certificate_fingerprint(cert)?;
    }

    Ok(response.text().await?)
}

2. 请求签名验证

use hmac::{Hmac, Mac};
use sha256::Sha256;

fn sign_request(method: &str, url: &str, body: &str, secret: &str) -> String {
    let message = format!("{}{}{}", method, url, body);
    let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes())
        .expect("HMAC can take key of any size");

    mac.update(message.as_bytes());
    let result = mac.finalize();
    let code_bytes = result.into_bytes();

    hex::encode(code_bytes)
}

浏览器自动化安全

1. 沙箱环境隔离

浏览器进程隔离

// 每个会话使用独立的浏览器上下文
pub async fn create_isolated_session(&mut self, session_id: &str)
    -> Result<(), Box<dyn std::error::Error>> {

    let context = self.browser.context_builder()
        .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...")
        .viewport(1920, 1080)
        .permissions(vec![])  // 禁用不必要的权限
        .build()
        .await?;

    // 设置安全策略
    context.add_init_script(r#"
        // 禁用危险的JavaScript API
        delete window.chrome;
        delete window.opera;
        delete navigator.webdriver;

        // 限制网络请求
        const originalFetch = window.fetch;
        window.fetch = function(url, options) {
            // 只允许Steam相关的域名
            if (!url.includes('steampowered.com') && !url.includes('steamcommunity.com')) {
                throw new Error('Blocked request to unauthorized domain');
            }
            return originalFetch(url, options);
        };
    "#).await?;

    self.contexts.insert(session_id.to_string(), context);
    Ok(())
}

资源限制

// 限制浏览器资源使用
let browser = playwright.chromium()
    .launcher()
    .args(vec![
        "--disable-dev-shm-usage".to_string(),
        "--disable-accelerated-2d-canvas".to_string(),
        "--max_old_space_size=4096".to_string(),  // 限制内存
        "--disable-background-timer-throttling".to_string(),
        "--disable-renderer-backgrounding".to_string(),
    ])
    .launch()
    .await?;

2. 反检测机制

模拟真实浏览器环境

// 在页面上下文中注入脚本来伪装环境
context.add_init_script(`
    // 修改navigator属性
    Object.defineProperty(navigator, 'webdriver', {
        get: () => undefined,
    });

    // 添加插件信息
    Object.defineProperty(navigator, 'plugins', {
        get: () => [
            { name: 'Chrome PDF Plugin', description: 'Portable Document Format', filename: 'internal-pdf-viewer' },
            { name: 'Chrome PDF Viewer', description: '', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai' },
            { name: 'Native Client', description: '', filename: 'internal-nacl-plugin' }
        ],
    });

    // 修改权限
    const originalQuery = window.navigator.permissions.query;
    window.navigator.permissions.query = (parameters) => (
        parameters.name === 'notifications' ?
            Promise.resolve({ state: Notification.permission }) :
            originalQuery(parameters)
    );
`).await?;

行为伪装

// 随机延迟和鼠标移动来模拟人类行为
async fn human_like_interaction(page: &Page, selector: &str) -> Result<(), Box<dyn std::error::Error>> {
    use rand::Rng;

    let mut rng = rand::thread_rng();

    // 随机延迟
    let delay = rng.gen_range(500..2000);
    tokio::time::sleep(tokio::time::Duration::from_millis(delay)).await;

    // 鼠标移动到元素
    let element = page.locator(selector).first().await?;
    let bounding_box = element.bounding_box().await?;
    let center_x = bounding_box.x + bounding_box.width / 2.0;
    let center_y = bounding_box.y + bounding_box.height / 2.0;

    // 模拟人类鼠标移动(贝塞尔曲线)
    page.mouse().move_to(center_x, center_y, None).await?;

    // 随机点击延迟
    let click_delay = rng.gen_range(100..500);
    tokio::time::sleep(tokio::time::Duration::from_millis(click_delay)).await;

    element.click().await?;
    Ok(())
}

运行时安全保护

1. 反调试检测

调试器检测

#[cfg(target_os = "windows")]
fn detect_debugger() -> bool {
    use winapi::um::debugapi::IsDebuggerPresent;
    unsafe { IsDebuggerPresent() != 0 }
}

#[cfg(target_os = "linux")]
fn detect_debugger() -> bool {
    use std::fs;
    if let Ok(content) = fs::read_to_string("/proc/self/status") {
        content.contains("TracerPid:\t") &&
        !content.contains("TracerPid:\t0")
    } else {
        false
    }
}

fn anti_debug_check() {
    if detect_debugger() {
        eprintln!("Debugger detected! Exiting for security.");
        std::process::exit(1);
    }
}

时间反调试

fn detect_timing_attack() -> bool {
    use std::time::Instant;

    let start = Instant::now();
    // 执行一些计算
    let mut sum = 0u64;
    for i in 0..1000000 {
        sum = sum.wrapping_add(i);
    }
    let elapsed = start.elapsed();

    // 如果执行时间异常(可能是调试器干扰),则认为是调试环境
    elapsed.as_millis() > 100  // 阈值可调整
}

2. 完整性校验

文件完整性检查

use ring::digest::{Context, SHA256};

fn calculate_file_hash(path: &str) -> Result<String, Box<dyn std::error::Error>> {
    let mut context = Context::new(&SHA256);
    let mut file = std::fs::File::open(path)?;

    let mut buffer = [0; 1024];
    loop {
        let bytes_read = file.read(&mut buffer)?;
        if bytes_read == 0 {
            break;
        }
        context.update(&buffer[..bytes_read]);
    }

    let digest = context.finish();
    Ok(hex::encode(digest.as_ref()))
}

fn verify_integrity(expected_hash: &str) -> bool {
    match calculate_file_hash(std::env::current_exe().unwrap().to_str().unwrap()) {
        Ok(actual_hash) => actual_hash == expected_hash,
        Err(_) => false,
    }
}

安全监控和响应

1. 异常检测

行为异常检测

struct SecurityMonitor {
    suspicious_activities: Vec<String>,
    max_attempts: u32,
    lockout_duration: std::time::Duration,
}

impl SecurityMonitor {
    fn new() -> Self {
        SecurityMonitor {
            suspicious_activities: Vec::new(),
            max_attempts: 5,
            lockout_duration: std::time::Duration::from_secs(300), // 5分钟
        }
    }

    fn record_activity(&mut self, activity: &str, severity: Severity) {
        let timestamp = chrono::Utc::now();
        let log_entry = format!("[{}] {}: {}", timestamp, severity, activity);

        self.suspicious_activities.push(log_entry);

        // 达到阈值时触发安全响应
        if self.suspicious_activities.len() >= self.max_attempts as usize {
            self.trigger_security_response();
        }
    }

    fn trigger_security_response(&self) {
        // 安全响应措施
        eprintln!("Security alert: Multiple suspicious activities detected!");

        // 清除敏感数据
        // 终止当前会话
        // 通知服务器
        // 记录安全事件
    }
}

2. 日志安全

安全日志记录

use log::{Level, LevelFilter, Record};

struct SecureLogger;

impl log::Log for SecureLogger {
    fn enabled(&self, metadata: &log::Metadata) -> bool {
        metadata.level() <= Level::Info
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            let timestamp = chrono::Utc::now();
            let level = record.level();
            let message = record.args();

            // 过滤敏感信息
            let safe_message = sanitize_log_message(&format!("{}", message));

            // 加密日志存储
            let encrypted_log = encrypt_log_entry(&safe_message);

            // 写入安全日志文件
            write_secure_log(&encrypted_log);
        }
    }

    fn flush(&self) {}
}

fn sanitize_log_message(message: &str) -> String {
    // 移除API密钥、密码等敏感信息
    message
        .replace(r"(?i)(api[_-]?key|password|token|secret)[\s]*[=:][\s]*[^\s]+", "[REDACTED]")
        .replace(r"\b\d{4}[\s\-]?\d{4}[\s\-]?\d{4}[\s\-]?\d{4}\b", "[CARD_NUMBER_REDACTED]")
}

部署安全

1. 构建时安全

代码混淆和优化

# Cargo.toml
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
panic = "abort"
strip = true

依赖审计

# 检查依赖的安全漏洞
cargo audit

# 检查过时的依赖
cargo outdated

# 生成依赖图
cargo tree

2. 运行时安全配置

系统权限最小化

// tauri.conf.json
{
  "tauri": {
    "allowlist": {
      "all": false,
      "fs": {
        "all": false,
        "readFile": true,
        "writeFile": true,
        "scope": ["$APPDATA/*"]
      },
      "http": {
        "all": false,
        "request": true,
        "scope": ["https://api.steampowered.com/*", "https://steamcommunity.com/*"]
      }
    }
  }
}

应急响应计划

1. 安全事件响应流程

  1. 检测:监控系统检测到异常
  2. 评估:分析事件严重程度
  3. 隔离:切断受影响组件
  4. 修复:应用安全补丁
  5. 恢复:逐步恢复服务
  6. 审查:分析根本原因

2. 数据泄露响应

立即响应措施

  • 撤销所有受影响的会话token
  • 强制所有用户重新认证
  • 通知受影响的用户
  • 更新加密密钥

长期改进措施

  • 代码安全审计
  • 加强访问控制
  • 改进监控系统
  • 用户教育培训

总结

通过多层安全架构、强类型语言、加密存储、网络安全保护、浏览器隔离、运行时监控等全方位安全措施,我们确保了系统的安全性:

  • 数据安全:AES-256加密 + PBKDF2密钥派生
  • 网络安全:HTTPS强制 + 请求签名
  • 运行时安全:反调试检测 + 完整性校验
  • 浏览器安全:沙箱隔离 + 行为伪装
  • 监控响应:实时监控 + 应急响应

这种深度防御策略确保了即使部分防护被突破,其他层面的防护仍能发挥作用。