安全最佳实践 #

一、安全开发原则 #

1.1 核心原则 #

原则 说明
最小权限 只授予必要的权限
深度防御 多层安全防护
安全默认 默认配置是安全的
失败安全 失败时保持安全状态
输入验证 验证所有输入

1.2 安全开发流程 #

text
┌─────────────────────────────────────────────────────────────┐
│                    安全开发流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  设计阶段 ──► 开发阶段 ──► 测试阶段 ──► 发布阶段            │
│      │           │           │           │                  │
│      ▼           ▼           ▼           ▼                  │
│  安全设计    安全编码    安全测试    安全审计                │
│  威胁建模    代码审查    渗透测试    依赖检查                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

二、输入验证 #

2.1 验证原则 #

rust
// 白名单验证
fn validate_username(username: &str) -> Result<String, String> {
    if username.len() < 3 || username.len() > 50 {
        return Err("Username must be 3-50 characters".to_string());
    }
    
    if !username.chars().all(|c| c.is_alphanumeric() || c == '_') {
        return Err("Username can only contain letters, numbers, and underscore".to_string());
    }
    
    Ok(username.to_string())
}

2.2 类型验证 #

rust
use serde::Deserialize;

#[derive(Deserialize)]
struct UserInput {
    #[serde(deserialize_with = "validate_email")]
    email: String,
    
    #[serde(deserialize_with = "validate_age")]
    age: u32,
}

fn validate_email<'de, D>(deserializer: D) -> Result<String, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let email: String = serde::Deserialize::deserialize(deserializer)?;
    
    if !email.contains('@') {
        return Err(serde::de::Error::custom("Invalid email format"));
    }
    
    Ok(email)
}

fn validate_age<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let age: u32 = serde::Deserialize::deserialize(deserializer)?;
    
    if age > 150 {
        return Err(serde::de::Error::custom("Invalid age"));
    }
    
    Ok(age)
}

2.3 路径验证 #

rust
use std::path::{Path, PathBuf};

fn validate_path(base: &Path, user_path: &str) -> Result<PathBuf, String> {
    let full_path = base.join(user_path);
    
    let canonical = full_path.canonicalize()
        .map_err(|_| "Invalid path")?;
    
    let base_canonical = base.canonicalize()
        .map_err(|_| "Invalid base path")?;
    
    if !canonical.starts_with(&base_canonical) {
        return Err("Path traversal detected".to_string());
    }
    
    Ok(canonical)
}

三、数据保护 #

3.1 敏感数据处理 #

rust
// 不要记录敏感数据
#[tauri::command]
fn login(username: String, password: String) -> Result<User, String> {
    // ❌ 不要记录密码
    // log::info!("Login: {} - {}", username, password);
    
    // ✅ 只记录必要信息
    log::info!("Login attempt for: {}", username);
    
    let user = verify_credentials(&username, &password)?;
    
    // 清除内存中的密码
    drop(password);
    
    Ok(user)
}

3.2 数据加密 #

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

fn encrypt_data(key: &[u8], data: &[u8]) -> Result<Vec<u8>, String> {
    let cipher = Aes256Gcm::new_from_slice(key)
        .map_err(|e| e.to_string())?;
    
    let nonce = Nonce::from_slice(b"unique nonce");
    
    cipher.encrypt(nonce, data)
        .map_err(|e| e.to_string())
}

fn decrypt_data(key: &[u8], encrypted: &[u8]) -> Result<Vec<u8>, String> {
    let cipher = Aes256Gcm::new_from_slice(key)
        .map_err(|e| e.to_string())?;
    
    let nonce = Nonce::from_slice(b"unique nonce");
    
    cipher.decrypt(nonce, encrypted)
        .map_err(|e| e.to_string())
}

3.3 安全存储 #

rust
use keyring::Entry;

fn store_secret(service: &str, key: &str, value: &str) -> Result<(), String> {
    let entry = Entry::new(service, key)
        .map_err(|e| e.to_string())?;
    
    entry.set_password(value)
        .map_err(|e| e.to_string())
}

fn get_secret(service: &str, key: &str) -> Result<String, String> {
    let entry = Entry::new(service, key)
        .map_err(|e| e.to_string())?;
    
    entry.get_password()
        .map_err(|e| e.to_string())
}

四、安全配置 #

4.1 最小权限配置 #

json
// capabilities/default.json
{
    "permissions": [
        "core:default",
        "fs:allow-read-text-file"
    ]
}

4.2 严格 CSP #

json
{
    "security": {
        "csp": "default-src 'self'; script-src 'self'; style-src 'self'"
    }
}

4.3 禁用危险功能 #

json
{
    "windows": [{
        "webPreferences": {
            "nodeIntegration": false,
            "contextIsolation": true,
            "sandbox": true
        }
    }]
}

五、错误处理 #

5.1 安全错误处理 #

rust
#[tauri::command]
fn safe_operation(input: String) -> Result<String, String> {
    // 不要暴露内部错误
    let result = internal_operation(&input)
        .map_err(|_| "Operation failed".to_string())?;
    
    Ok(result)
}

fn internal_operation(input: &str) -> Result<String, Box<dyn std::error::Error>> {
    // 内部错误详情
    // ...
    Ok(format!("Processed: {}", input))
}

5.2 错误日志 #

rust
use log::{error, info};

#[tauri::command]
fn operation_with_logging(input: String) -> Result<String, String> {
    info!("Operation started");
    
    match internal_operation(&input) {
        Ok(result) => {
            info!("Operation succeeded");
            Ok(result)
        }
        Err(e) => {
            error!("Operation failed: {}", e);  // 详细日志
            Err("Operation failed".to_string())  // 简化错误返回
        }
    }
}

六、依赖安全 #

6.1 依赖审计 #

bash
# Rust 依赖审计
cargo audit

# 前端依赖审计
npm audit
pnpm audit
yarn audit

6.2 锁定依赖版本 #

toml
# Cargo.toml
[dependencies]
serde = "1.0.190"  # 锁定版本
json
// package.json
{
    "dependencies": {
        "react": "18.2.0"  // 锁定版本
    }
}

6.3 定期更新 #

bash
# 更新 Rust 依赖
cargo update

# 更新前端依赖
pnpm update

七、安全测试 #

7.1 安全测试清单 #

markdown
## 输入验证测试
- [ ] 测试各种边界输入
- [ ] 测试特殊字符
- [ ] 测试超长输入
- [ ] 测试空输入

## 权限测试
- [ ] 测试未授权访问
- [ ] 测试权限边界
- [ ] 测试权限提升

## 注入测试
- [ ] SQL 注入测试
- [ ] XSS 测试
- [ ] 命令注入测试
- [ ] 路径遍历测试

7.2 自动化安全测试 #

yaml
# .github/workflows/security.yml
name: Security Audit

on: [push, pull_request]

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Rust Audit
        run: cargo audit
      
      - name: NPM Audit
        run: npm audit

八、安全审计清单 #

8.1 代码审计 #

markdown
## 输入处理
- [ ] 所有用户输入都经过验证
- [ ] 使用白名单验证
- [ ] 限制输入长度
- [ ] 正确处理特殊字符

## 输出处理
- [ ] HTML 实体编码
- [ ] URL 编码
- [ ] JavaScript 编码
- [ ] SQL 参数化

## 认证授权
- [ ] 强密码策略
- [ ] 安全会话管理
- [ ] 权限检查
- [ ] 登出功能

## 数据保护
- [ ] 敏感数据加密
- [ ] 安全存储
- [ ] 传输加密
- [ ] 数据清理

8.2 配置审计 #

markdown
## 权限配置
- [ ] 最小权限原则
- [ ] 权限范围限制
- [ ] 无多余权限

## CSP 配置
- [ ] 严格 CSP
- [ ] 无 unsafe-*
- [ ] 限制外部资源

## 安全特性
- [ ] 上下文隔离
- [ ] 沙箱模式
- [ ] 禁用开发工具

九、总结 #

9.1 核心要点 #

要点 说明
输入验证 验证所有输入
数据保护 加密敏感数据
最小权限 只授予必要权限
安全配置 严格安全设置
定期审计 检查代码和依赖

9.2 下一步 #

现在你已经掌握了安全最佳实践,接下来让我们学习 打包配置,了解如何打包发布 Tauri 应用!

最后更新:2026-03-28