Skip to content

Tauri 安全机制

安全架构

┌─────────────────────────────────────────────────────┐
│                    Frontend                          │
│              (受限的 Web 环境)                        │
└─────────────────────────┬───────────────────────────┘
                          │ 权限检查
┌─────────────────────────▼───────────────────────────┐
│              Tauri Permission System                 │
│  ┌──────────────────────────────────────────────────┐│
│  │  Capabilities (能力集) → Permissions (权限)      ││
│  │  → Scopes (作用域限制)                           ││
│  └──────────────────────────────────────────────────┘│
└─────────────────────────┬───────────────────────────┘
                          │ 验证通过
┌─────────────────────────▼───────────────────────────┐
│                  Rust Backend                        │
│              (安全的系统操作)                         │
└─────────────────────────────────────────────────────┘

权限系统

定义 Capabilities

json
// src-tauri/capabilities/main.json
{
  "identifier": "main-capability",
  "description": "Main window capabilities",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "shell:allow-open",
    {
      "identifier": "fs:allow-read",
      "allow": [
        { "path": "$APPDATA/**" },
        { "path": "$DOCUMENT/**" }
      ]
    },
    {
      "identifier": "fs:allow-write",
      "allow": [
        { "path": "$APPDATA/**" }
      ]
    },
    "http:default"
  ]
}

权限作用域

json
// 文件系统权限限制
{
  "identifier": "fs:allow-read",
  "allow": [
    { "path": "$APPDATA/**" },      // 应用数据目录
    { "path": "$DOCUMENT/myapp/**" } // 文档目录下特定文件夹
  ],
  "deny": [
    { "path": "$HOME/.ssh/**" }      // 禁止读取 SSH 密钥
  ]
}

CSP 配置

json
// tauri.conf.json
{
  "app": {
    "security": {
      "csp": {
        "default-src": "'self'",
        "script-src": "'self'",
        "style-src": "'self' 'unsafe-inline'",
        "img-src": "'self' data: https:",
        "connect-src": "'self' https://api.example.com",
        "font-src": "'self' data:"
      }
    }
  }
}

IPC 安全

Command 权限验证

rust
use tauri::{command, AppHandle, Manager};

// 定义权限插件
#[command]
async fn admin_action(app: AppHandle) -> Result<(), String> {
    // 验证调用来源
    let window = app.get_window("main").ok_or("Invalid window")?;
    
    // 检查用户权限
    let state = app.state::<AuthState>();
    if !state.is_admin() {
        return Err("Permission denied".to_string());
    }
    
    // 执行敏感操作
    Ok(())
}

输入验证

rust
use validator::Validate;

#[derive(Validate, serde::Deserialize)]
struct CreateUserInput {
    #[validate(email)]
    email: String,
    
    #[validate(length(min = 8, max = 128))]
    password: String,
    
    #[validate(length(max = 100))]
    name: String,
}

#[command]
fn create_user(input: CreateUserInput) -> Result<(), String> {
    input.validate().map_err(|e| e.to_string())?;
    // 处理已验证的输入
    Ok(())
}

安全存储

rust
use tauri_plugin_store::StoreBuilder;
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())?;
    Ok(())
}

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())
}

HTTPS 和证书

rust
// 强制 HTTPS
#[command]
async fn secure_fetch(url: String) -> Result<String, String> {
    if !url.starts_with("https://") {
        return Err("Only HTTPS URLs are allowed".to_string());
    }
    
    let client = reqwest::Client::builder()
        .min_tls_version(reqwest::tls::Version::TLS_1_2)
        .build()
        .map_err(|e| e.to_string())?;
    
    client.get(&url)
        .send()
        .await
        .map_err(|e| e.to_string())?
        .text()
        .await
        .map_err(|e| e.to_string())
}

更新安全

json
// tauri.conf.json
{
  "plugins": {
    "updater": {
      "endpoints": [
        "https://releases.myapp.com/{{target}}/{{arch}}/{{current_version}}"
      ],
      "pubkey": "YOUR_PUBLIC_KEY_HERE"
    }
  }
}
rust
// 签名验证
// 1. 生成密钥对
// tauri signer generate -w ~/.tauri/myapp.key

// 2. 构建时签名
// TAURI_SIGNING_PRIVATE_KEY=~/.tauri/myapp.key tauri build

安全最佳实践

rust
// 1. 最小权限原则
// 只请求必需的权限

// 2. 避免 eval 和动态代码
// CSP 禁用 unsafe-eval

// 3. 敏感操作双重验证
#[command]
async fn delete_account(
    password: String,
    state: State<'_, AuthState>,
) -> Result<(), String> {
    // 二次验证密码
    state.verify_password(&password)?;
    // 执行删除
    Ok(())
}

// 4. 日志脱敏
fn log_request(url: &str, headers: &Headers) {
    let sanitized_url = url.replace(|c: char| !c.is_ascii(), "*");
    log::info!("Request to: {}", sanitized_url);
    // 不记录敏感 header
}

面试要点

  1. 权限系统:Capabilities → Permissions → Scopes 三层模型
  2. CSP 配置:限制脚本来源、网络请求
  3. IPC 安全:输入验证、权限检查
  4. 安全存储:系统密钥链、加密存储

前端面试知识库