CherryHQ/cherry-studio数据销毁:敏感数据清除

【免费下载链接】cherry-studio 🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端 【免费下载链接】cherry-studio 项目地址: https://gitcode.com/CherryHQ/cherry-studio

概述

Cherry Studio作为一款支持多个LLM(Large Language Model,大语言模型)提供商的桌面客户端,在处理AI对话、知识库管理、文件操作等场景时会存储大量敏感数据。本文深入探讨Cherry Studio的数据存储机制、敏感数据识别方法,以及如何安全彻底地销毁这些数据,确保用户隐私和安全。

数据存储架构分析

核心数据目录结构

Cherry Studio采用Electron框架构建,其数据存储遵循Electron应用的标准模式,主要存储在以下目录:

mermaid

敏感数据类型识别

根据Cherry Studio的功能特性,需要重点关注以下敏感数据类型:

数据类型 存储位置 敏感级别 说明
对话历史 Data/Chats 🔴 高 包含所有AI对话内容
API密钥 Config/ 🔴 高 LLM提供商API凭证
知识库文件 Data/Knowledge/ 🔴 高 上传的文档和知识
本地笔记 Data/Notes/ 🟡 中 用户创建的笔记
缓存数据 Cache/ 🟡 中 临时处理结果
备份文件 Backup/ 🟡 中 自动备份数据

数据销毁实施方案

手动清除方案

1. 基础数据目录清除
# 获取用户数据目录路径
USER_DATA_DIR=~/Library/Application Support/CherryStudio  # macOS
# 或
USER_DATA_DIR=~/.config/CherryStudio  # Linux
# 或
USER_DATA_DIR=%APPDATA%\CherryStudio  # Windows

# 彻底删除核心数据目录
rm -rf "$USER_DATA_DIR/Data"
rm -rf "$USER_DATA_DIR/Cache"
rm -rf "$USER_DATA_DIR/Backups"

# 清除配置文件(保留设置但删除敏感信息)
find "$USER_DATA_DIR" -name "*.json" -exec grep -l "api_key\|token\|secret" {} \; -delete
2. 特定敏感数据清除

对于需要选择性清除的场景,可以使用以下针对性脚本:

const fs = require('fs-extra');
const path = require('path');
const crypto = require('crypto');

class CherryDataWiper {
    constructor(userDataPath) {
        this.userDataPath = userDataPath;
        this.sensitivePatterns = [
            /api[_-]?key/i,
            /token/i,
            /secret/i,
            /password/i,
            /credential/i
        ];
    }

    // 安全擦除文件内容
    async secureWipeFile(filePath) {
        try {
            const stats = await fs.stat(filePath);
            const fileSize = stats.size;
            
            // 三次覆盖写入随机数据
            const randomBuffer = crypto.randomBytes(fileSize);
            await fs.writeFile(filePath, randomBuffer);
            await fs.writeFile(filePath, crypto.randomBytes(fileSize));
            await fs.writeFile(filePath, crypto.randomBytes(fileSize));
            
            // 最终删除文件
            await fs.unlink(filePath);
        } catch (error) {
            console.warn(`无法安全擦除文件: ${filePath}`, error);
        }
    }

    // 递归清除敏感数据
    async cleanSensitiveData(dirPath) {
        try {
            const items = await fs.readdir(dirPath);
            
            for (const item of items) {
                const fullPath = path.join(dirPath, item);
                const stat = await fs.stat(fullPath);
                
                if (stat.isDirectory()) {
                    await this.cleanSensitiveData(fullPath);
                } else if (stat.isFile()) {
                    // 检查文件扩展名
                    const ext = path.extname(item).toLowerCase();
                    const isConfigFile = ['.json', '.yml', '.yaml', '.conf', '.config'].includes(ext);
                    
                    if (isConfigFile) {
                        await this.processConfigFile(fullPath);
                    }
                }
            }
        } catch (error) {
            console.warn(`清理目录时出错: ${dirPath}`, error);
        }
    }

    // 处理配置文件中的敏感信息
    async processConfigFile(filePath) {
        try {
            let content = await fs.readFile(filePath, 'utf8');
            let modified = false;
            
            // 移除敏感键值对
            this.sensitivePatterns.forEach(pattern => {
                const regex = new RegExp(`"(${pattern.source}[^"]*)":\\s*"[^"]*"`, 'gi');
                content = content.replace(regex, '"$1": "REDACTED"');
                modified = modified || regex.test(content);
            });
            
            if (modified) {
                await fs.writeFile(filePath, content);
                console.log(`已清理敏感信息: ${filePath}`);
            }
        } catch (error) {
            console.warn(`处理配置文件失败: ${filePath}`, error);
        }
    }
}

自动化销毁工具

集成销毁功能

Cherry Studio可以集成以下自动化数据销毁功能:

// 在BackupManager中添加数据销毁方法
import * as fs from 'fs-extra';
import * as path from 'path';
import { app } from 'electron';

export class DataDestructionService {
    private userDataPath: string;
    
    constructor() {
        this.userDataPath = app.getPath('userData');
    }

    // 完全销毁所有数据
    async destroyAllData(): Promise<void> {
        const dataPaths = [
            path.join(this.userDataPath, 'Data'),
            path.join(this.userDataPath, 'Cache'),
            path.join(this.userDataPath, 'Backups'),
            path.join(this.userDataPath, 'Logs')
        ];

        for (const dataPath of dataPaths) {
            if (await fs.pathExists(dataPath)) {
                await this.secureDeleteDirectory(dataPath);
            }
        }
    }

    // 安全删除目录(多次覆盖)
    private async secureDeleteDirectory(dirPath: string): Promise<void> {
        // 首先递归删除所有文件内容
        await this.wipeDirectoryContents(dirPath);
        
        // 然后删除目录结构
        await fs.remove(dirPath);
    }

    // 擦除目录内容
    private async wipeDirectoryContents(dirPath: string): Promise<void> {
        const items = await fs.readdir(dirPath);
        
        for (const item of items) {
            const fullPath = path.join(dirPath, item);
            const stat = await fs.stat(fullPath);
            
            if (stat.isDirectory()) {
                await this.wipeDirectoryContents(fullPath);
            } else {
                await this.secureWipeFile(fullPath);
            }
        }
    }

    // 安全擦除单个文件
    private async secureWipeFile(filePath: string): Promise<void> {
        const fileSize = (await fs.stat(filePath)).size;
        
        // 三次覆盖写入
        const randomData = Buffer.alloc(fileSize);
        for (let i = 0; i < 3; i++) {
            crypto.randomFillSync(randomData);
            await fs.writeFile(filePath, randomData);
        }
        
        // 最终删除
        await fs.unlink(filePath);
    }

    // 选择性数据销毁
    async selectiveDestruction(options: {
        chatHistory?: boolean;
        apiKeys?: boolean;
        knowledgeBase?: boolean;
        cache?: boolean;
        backups?: boolean;
    }): Promise<void> {
        const destructionTasks: Promise<void>[] = [];

        if (options.chatHistory) {
            destructionTasks.push(this.destroyChatHistory());
        }
        
        if (options.apiKeys) {
            destructionTasks.push(this.destroyApiKeys());
        }
        
        if (options.knowledgeBase) {
            destructionTasks.push(this.destroyKnowledgeBase());
        }
        
        if (options.cache) {
            destructionTasks.push(this.destroyCache());
        }
        
        if (options.backups) {
            destructionTasks.push(this.destroyBackups());
        }

        await Promise.all(destructionTasks);
    }

    private async destroyChatHistory(): Promise<void> {
        const chatPath = path.join(this.userDataPath, 'Data', 'Chats');
        if (await fs.pathExists(chatPath)) {
            await this.secureDeleteDirectory(chatPath);
        }
    }

    private async destroyApiKeys(): Promise<void> {
        const configPath = path.join(this.userDataPath, 'Config');
        if (await fs.pathExists(configPath)) {
            await this.wipeSensitiveConfigs(configPath);
        }
    }

    private async wipeSensitiveConfigs(configPath: string): Promise<void> {
        // 实现配置文件中敏感信息的擦除逻辑
    }
}

安全最佳实践

数据生命周期管理

mermaid

加密存储策略

对于特别敏感的数据,建议采用以下加密策略:

// 敏感数据加密存储示例
import * as crypto from 'crypto';

class SecureStorage {
    private encryptionKey: Buffer;
    
    constructor() {
        // 从安全的地方获取加密密钥
        this.encryptionKey = this.deriveEncryptionKey();
    }

    private deriveEncryptionKey(): Buffer {
        // 使用用户特定的信息派生密钥
        const salt = crypto.randomBytes(16);
        return crypto.pbkdf2Sync(
            process.env.USER_SPECIFIC_SECRET || 'default-secret',
            salt,
            100000,
            32,
            'sha512'
        );
    }

    async encryptSensitiveData(data: string): Promise<string> {
        const iv = crypto.randomBytes(16);
        const cipher = crypto.createCipheriv('aes-256-gcm', this.encryptionKey, iv);
        
        const encrypted = Buffer.concat([
            cipher.update(data, 'utf8'),
            cipher.final()
        ]);
        
        const authTag = cipher.getAuthTag();
        
        return JSON.stringify({
            iv: iv.toString('base64'),
            data: encrypted.toString('base64'),
            tag: authTag.toString('base64')
        });
    }

    async decryptSensitiveData(encryptedData: string): Promise<string> {
        const { iv, data, tag } = JSON.parse(encryptedData);
        
        const decipher = crypto.createDecipheriv(
            'aes-256-gcm',
            this.encryptionKey,
            Buffer.from(iv, 'base64')
        );
        
        decipher.setAuthTag(Buffer.from(tag, 'base64'));
        
        const decrypted = Buffer.concat([
            decipher.update(Buffer.from(data, 'base64')),
            decipher.final()
        ]);
        
        return decrypted.toString('utf8');
    }
}

合规性与审计

数据销毁审计日志

建立完善的数据销毁审计机制:

interface DestructionAuditLog {
    timestamp: Date;
    operation: 'full_destruction' | 'selective_destruction';
    target: string[];
    initiatedBy: string; // user or system
    result: 'success' | 'partial' | 'failed';
    details: string;
    checksum?: string; // 用于验证销毁完整性
}

class DestructionAuditor {
    private logFile: string;
    
    constructor() {
        this.logFile = path.join(app.getPath('userData'), 'audit', 'destruction.log');
    }

    async logDestruction(event: Omit<DestructionAuditLog, 'timestamp'>): Promise<void> {
        const auditEntry: DestructionAuditLog = {
            timestamp: new Date(),
            ...event
        };

        await fs.ensureDir(path.dirname(this.logFile));
        await fs.appendFile(
            this.logFile,
            JSON.stringify(auditEntry) + '\n',
            'utf8'
        );
    }

    // 生成销毁验证摘要
    async generateDestructionProof(directory: string): Promise<string> {
        const fileHashes: string[] = [];
        
        const collectHashes = async (dir: string) => {
            const items = await fs.readdir(dir);
            for (const item of items) {
                const fullPath = path.join(dir, item);
                const stat = await fs.stat(fullPath);
                
                if (stat.isDirectory()) {
                    await collectHashes(fullPath);
                } else {
                    const content = await fs.readFile(fullPath);
                    fileHashes.push(crypto.createHash('sha256').update(content).digest('hex'));
                }
            }
        };
        
        await collectHashes(directory);
        return crypto.createHash('sha256').update(fileHashes.sort().join('')).digest('hex');
    }
}

总结

Cherry Studio的数据销毁机制需要从多个层面进行考虑:

  1. 识别敏感数据:明确各类数据的敏感级别和存储位置
  2. 分级处理:根据敏感程度采用不同的销毁策略
  3. 安全擦除:使用多次覆盖写入确保数据不可恢复
  4. 审计追踪:记录所有销毁操作以备审计
  5. 用户控制:提供清晰的用户界面控制数据生命周期

通过实施上述方案,可以确保Cherry Studio用户的数据在需要时能够被安全、彻底地销毁,满足隐私保护和合规性要求。在实际部署时,建议根据具体的使用场景和安全要求调整销毁策略和加密强度。

【免费下载链接】cherry-studio 🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端 【免费下载链接】cherry-studio 项目地址: https://gitcode.com/CherryHQ/cherry-studio

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐