mcpField
U
understanding-tauri-ipc

understanding-tauri-ipc

github.com/dchuk开发36188 次安装40 周前更新
代码仓库

关于 understanding-tauri-ipc

这项开发技能讲解 Tauri 前端与 Rust 后端之间的 IPC 机制及安全模式,涵盖事件和命令消息,并比较默认的 brownfield 与沙箱隔离方案。

主要功能

  1. 01讲解事件和命令消息
  2. 02比较 brownfield 与隔离模式
  3. 03验证并加密 IPC 消息
  4. 04配置沙箱隔离
  5. 05应用 IPC 安全实践

适用场景

  1. 01将现有 Web 应用迁移到桌面端
  2. 02为 Tauri 应用选择 IPC 模式
  3. 03保护敏感操作免受前端调用

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dchuk/claude-code-tauri-skills --skill understanding-tauri-ipc

安装后在 Claude Code 中输入/understanding-tauri-ipc确认已加载。

详细介绍

Tauri 进程间通信(IPC)

本技能介绍 Tauri 的 IPC 系统,包括用于前端与后端进程安全通信的 Brownfield 和 Isolation 模式。

概述

Tauri 使用异步消息传递实现进程间通信。借助此机制,隔离的进程可以安全地交换序列化的请求和响应。

为什么使用消息传递?

  • 比共享内存或直接访问函数更安全
  • 接收方可以拒绝或丢弃恶意请求
  • Tauri Core 会在执行前验证所有请求
  • 防止未经授权的函数调用

IPC 基元

Tauri 提供两种 IPC 基元:

事件
  • 方向:双向(前端 <-> Tauri Core)
  • 类型:即发即弃的单向消息传递
  • 适用场景:生命周期事件、状态变更、通知

Rust(发送到前端):

use tauri::{AppHandle, Emitter};

fn emit_event(app: &AppHandle) {
    app.emit("backend-event", "payload data").unwrap();
}

前端(监听):

import { listen } from '@tauri-apps/api/event';

const unlisten = await listen('backend-event', (event) => {
  console.log('Received:', event.payload);
});

// Call unlisten() when done

前端(发送到后端):

import { emit } from '@tauri-apps/api/event';

await emit('frontend-event', { data: 'value' });
命令
  • 方向:前端 -> Rust 后端
  • 协议:基于 JSON-RPC 的抽象
  • API:类似于浏览器的 fetch() API
  • 要求:参数和返回数据必须可序列化为 JSON

Rust 命令定义:

#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

前端调用:

import { invoke } from '@tauri-apps/api/core';

const greeting = await invoke('greet', { name: 'World' });
console.log(greeting); // "Hello, World!"

带有 Result 的异步命令:

#[tauri::command]
async fn read_file(path: String) -> Result<String, String> {
    std::fs::read_to_string(&path)
        .map_err(|e| e.to_string())
}

IPC 模式

Tauri 提供两种 IPC 安全模式:Brownfield(默认)和 Isolation。


Brownfield 模式

定义

Brownfield 模式是 Tauri 的默认 IPC 方式。它只需进行极少修改,即可兼容现有 Web 前端项目。

适用场景
  • 将现有 Web 应用迁移到桌面端
  • 快速原型设计和开发
  • 前端代码可信的应用
  • IPC 接口范围有限的简单应用
优势
  • 无需配置
  • 对现有 Web 代码的改动极少
  • 可直接访问 Tauri API
  • 开发速度最快
配置

Brownfield 是默认模式。显式配置为可选项:

{
  "app": {
    "security": {
      "pattern": {
        "use": "brownfield"
      }
    }
  }
}

**注意:**Brownfield 没有其他配置选项。

代码示例

Rust 后端:

#[tauri::command]
fn process_data(input: String) -> Result<String, String> {
    // Direct processing without isolation layer
    Ok(format!("Processed: {}", input))
}

前端:

import { invoke } from '@tauri-apps/api/core';

// Direct invocation - no isolation layer
const result = await invoke('process_data', { input: 'test' });
安全注意事项
  • 前端代码可以直接访问所有已公开的命令
  • 前端与后端之间没有额外的验证层
  • 前端依赖项中的供应链攻击可能会调用命令
  • 依赖 Rust 中的命令级验证

Isolation 模式

定义

Isolation 模式会在 Tauri Core 收到前端发来的消息之前,使用 JavaScript 拦截并修改所有 Tauri API 消息。一个安全的 JavaScript 应用(Isolation 应用)会在沙箱 iframe 中运行,用于验证并加密所有 IPC 通信。

适用场景
  • 前端依赖项较多的应用
  • 安全要求较高的应用
  • 处理敏感数据或操作
  • 面向公众的应用
  • 需要防范供应链攻击的场景
优势

防范开发过程中的威胁:

  • 在执行前验证所有 IPC 调用
  • 捕获恶意或不需要的前端调用
  • 降低供应链攻击风险
  • 为所有通信提供检查点

Tauri 建议在可行时使用 Isolation。

工作原理
  1. Tauri 的 IPC 处理程序接收来自前端的消息
  2. 消息路由到 Isolation 应用(沙箱 iframe)
  3. Isolation hook 验证消息,并可能对其进行修改
  4. 使用运行时生成的密钥通过 AES-GCM 加密消息
  5. 加密后的消息返回 IPC 处理程序
  6. 加密后的消息传递给 Tauri Core,以便解密和执行

关键安全特性:

  • 每次启动应用时都会生成新的加密密钥
  • 沙箱 iframe 可防止 Isolation 代码被篡改
  • 验证所有 IPC 调用,包括基于事件的 API
配置

tauri.conf.json:

{
  "app": {
    "security": {
      "pattern": {
        "use": "isolation",
        "options": {
          "dir": "../dist-isolation"
        }
      }
    }
  }
}
代码示例

目录结构:

project/
  src/           # Main frontend
  src-tauri/     # Rust backend
  dist-isolation/
    index.html
    index.js

dist-isolation/index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Isolation Secure Script</title>
  </head>
  <body>
    <script src="index.js"></script>
  </body>
</html>

dist-isolation/index.js:

window.__TAURI_ISOLATION_HOOK__ = (payload) => {
  // Log all IPC calls for debugging
  console.log('IPC call intercepted:', payload);

  // Return payload unchanged (passthrough)
  return payload;
};

验证示例(index.js):

window.__TAURI_ISOLATION_HOOK__ = (payload) => {
  // Validate command calls
  if (payload.cmd === 'invoke') {
    const { __tauriModule, message } = payload;

    // Block unauthorized file system access
    if (message.cmd === 'readFile') {
      const path = message.path;
      if (!path.startsWith('/allowed/directory/')) {
        console.error('Blocked unauthorized file access:', path);
        return null; // Block the request
      }
    }

    // Validate specific commands
    if (message.cmd === 'deleteItem') {
      if (!confirm('Are you sure you want to delete this item?')) {
        return null; // User cancelled
      }
    }
  }

  return payload;
};

全面验证示例:

const ALLOWED_COMMANDS = ['greet', 'read_config', 'save_settings'];
const BLOCKED_PATHS = ['/etc/', '/usr/', '/System/'];

window.__TAURI_ISOLATION_HOOK__ = (payload) => {
  // Validate invoke commands
  if (payload.cmd === 'invoke') {
    const commandName = payload.message?.cmd;

    // Whitelist approach
    if (!ALLOWED_COMMANDS.includes(commandName)) {
      console.warn('Blocked unknown command:', commandName);
      return null;
    }

    // Validate path arguments
    const args = payload.message?.args || {};
    if (args.path) {
      for (const blocked of BLOCKED_PATHS) {
        if (args.path.startsWith(blocked)) {
          console.error('Blocked access to protected path:', args.path);
          return null;
        }
      }
    }
  }

  // Validate event emissions
  if (payload.cmd === 'emit') {
    const eventName = payload.event;
    // Add event validation as needed
  }

  return payload;
};
性能注意事项
  • 对于大多数应用,AES-GCM 加密带来的开销很小
  • 与 HTTPS 中使用的 TLS 加密相当
  • 密钥生成需要系统熵(在现代系统上可无缝处理)
  • 对性能敏感的应用可能会注意到轻微影响
限制
  • 在 Windows 上,ES Modules 无法在沙盒 iframe 中加载
  • 脚本必须在构建时内联
  • 外部文件必须嵌入,而不能引用
  • 隔离应用应避免使用打包工具
最佳实践
  1. 保持简单:尽量减少隔离应用的依赖项
  2. 不使用打包工具:跳过 ES Modules 和复杂的构建流程
  3. 验证输入:确认 IPC 调用符合预期参数
  4. 将命令列入白名单:仅允许已知且安全的命令
  5. 记录可疑活动:监控潜在攻击
  6. 应用于事件:验证会触发 Rust 代码的事件

模式比较

方面BrownfieldIsolation
默认是否
配置无需配置需要隔离应用
安全性基础增强
验证仅命令级别所有 IPC 调用
加密无AES-GCM
性能最快略有开销
复杂度简单中等
适用场景可信代码、原型生产环境、敏感应用

安全最佳实践

两种模式都适用
  1. 在 Rust 命令中验证所有输入

    #[tauri::command]
    fn process_file(path: String) -> Result<String, String> {
        // Always validate paths
        if path.contains("..") || path.starts_with("/etc") {
            return Err("Invalid path".into());
        }
        // Process file...
        Ok("Done".into())
    }
    
  2. 使用类型化参数

    #[derive(serde::Deserialize)]
    struct CreateUserArgs {
        name: String,
        email: String,
    }
    
    #[tauri::command]
    fn create_user(args: CreateUserArgs) -> Result<(), String> {
        // Type-safe argument handling
        Ok(())
    }
    
  3. 限制公开的命令:仅公开必要功能

  4. 使用基于能力的权限:在 capabilities/ 中配置权限

Isolation 模式适用
  1. 尽量精简隔离代码:缩小攻击面
  2. 避免外部依赖项:隔离应用中不使用 npm 包
  3. 严格验证:优先使用白名单,而非黑名单
  4. 充分测试:确保验证能捕获边界情况

选择模式

在以下情况下使用 Brownfield:

  • 构建内部工具
  • 快速制作原型
  • 前端代码完全可信
  • 安全要求较低

在以下情况下使用 Isolation:

  • 构建面向公众的应用
  • 处理敏感用户数据
  • 使用许多第三方前端包
  • 安全性是首要考虑
  • 有合规要求

如无法确定,生产应用优先选择 Isolation。

评论

支持 Markdown