OpenWhispr 安全模型解析:从漏洞披露流程到本地优先的凭据加密架构
【免费下载链接】openwhisprVoice-to-text dictation app with local (Nvidia Parakeet/Whisper) and cloud models (BYOK). Privacy-first and available cross-platform.项目地址: https://gitcode.com/GitHub_Trending/op/openwhispr
OpenWhispr 是一款基于 Electron 的跨平台语音转文字(dictation)应用,支持本地模型(NVIDIA Parakeet / whisper.cpp)与自带密钥(BYOK)的云端模型,主打隐私优先。本文以仓库根目录的 SECURITY.md 安全策略文档为主体,结合src/helpers/secretCrypto.js、src/helpers/environment.js、src/helpers/windowConfig.js、preload.js等源码实现,系统讲解其受支持版本策略、漏洞报告流程、安全边界(Scope)定义,以及"本地优先音频处理 + 操作系统密钥链凭据加密 + 上下文隔离"的完整安全模型,帮助你理解该应用的威胁模型与安全工程实践。
受支持版本与安全更新策略
与多数开源项目一样,OpenWhispr 只为仍在维护周期内的版本提供安全修复。当前仓库 SECURITY.md 中定义的版本支持矩阵如下:
| 版本 | 支持状态 |
|---|---|
| 1.7.x | ✅ 受支持 |
| < 1.7 | ❌ 不受支持 |
从源码看,仓库当前package.json中声明的版本为1.10.0,即当前处于 1.7.x 维护线之上。这意味着:
- 仅 1.7.x 及更高版本会收到安全补丁;低于 1.7 的历史版本不再维护,若你仍在使用旧版本,建议升级到受支持的 1.7.x 及以上版本后再继续使用,否则已知漏洞将不会被修复。
- 安全修复会随版本发布进入 CHANGELOG.md,你可以据此判断当前版本是否已包含某项修复。
漏洞报告流程与响应承诺
报告渠道:不要公开提交 Issue
请不要为安全漏洞创建公开的 Issue。这属于开源项目安全响应的通用最佳实践:公开披露细节会放大被利用的风险,为攻击者提供"免费武器"。SECURITY.md 提供了两条私有报告渠道:
- GitHub 的私有漏洞报告(Private Vulnerability Reporting)功能;
- 通过邮件 security@openwhispr.com 提交。
报告内容建议包含:受影响的版本号、漏洞类型与危害描述、可复现步骤(PoC)、以及你建议的修复方向。信息越完整,维护者复现与修复的速度越快。
响应 SLA
维护团队承诺以下处理时间线:
- 48 小时内确认收到报告(acknowledge);
- 关键(critical)漏洞目标在 7 天内发布修复。
需要说明的是,这是项目对安全研究员的时间承诺,实际修复周期取决于漏洞的复杂度与复现难度,7 天是"目标"而非保证。
漏洞范围(Scope)定义
安全策略文档通过"范围内 / 范围外"两列表明确定义了安全团队愿意受理的漏洞类型,这本质上就是项目维护者认可的威胁模型。
范围内(In Scope)
| 类别 | 说明 | 源码侧对应面 |
|---|---|---|
| 通过构造的音频文件或转写输出实现远程代码执行(RCE) | 恶意音频样本或恶意转写文本试图突破应用进程边界 | 音频文件上传与转写链路:transcribeAudioFile、CLI 桥的POST /v1/transcribe(见 src/helpers/cliBridge.js),以及 whisper.cpp / sherpa-onnx 等本地推理运行时 |
| 通过原生二进制(按键监听、粘贴辅助工具)实现提权 | 平台级辅助程序被滥用 | Windows 键盘监听器、macOS 麦克风监听器、各平台粘贴工具(见 native/meeting-aec-helper 与scripts/download-*系列) |
| 凭据泄露(API 密钥、OAuth 令牌、数据库凭据) | 密钥落盘方式不当导致泄露 | 由secretCrypto.js+environment.js构成的凭据加密存储体系 |
| Electron 渲染进程中的跨站脚本(XSS) | 渲染进程被注入恶意脚本 | 上下文隔离(contextIsolation)、受限 preload 桥(preload.js) |
| 主进程与渲染进程之间的不安全 IPC | IPC 通道缺乏校验导致越权 | 受限的contextBridge.exposeInMainWorld暴露面(preload.js) |
| 通过依赖或原生编译引发的供应链攻击 | 依赖投毒、构建脚本被篡改 | package-lock.json锁定的依赖、从源码编译的原生二进制 |
范围外(Out of Scope)
- 需要对已解锁机器进行物理访问的问题:物理接触等于完全控制,此类问题属于物理安全范畴;
- 针对本地应用的拒绝服务(DoS):本地 DoS 通常只影响使用者本人,危害有限;
- 社会工程攻击:诱导用户安装恶意软件或泄露信息不属于代码漏洞。
理解这份 Scope 的价值在于:安全研究员可以据此判断某个发现是否值得提交,而使用者也可以据此评估"哪些攻击面是项目明确关注的、哪些是明确不在意的"。
安全模型(Security Model)源码级解读
SECURITY.md 用四条支柱概括了 OpenWhispr 的安全设计,下面逐条结合仓库源码展开。
1. 本地优先的音频处理
Audio is transcribed on-device using whisper.cpp or NVIDIA Parakeet. Recordings are not sent to external servers unless explicitly configured by the user.
OpenWhispr 的核心语音链路默认完全在本地完成:
- whisper.cpp:通过 src/helpers/whisper.js 封装原生二进制,模型存储在
~/.cache/openwhispr/whisper-models/,音频通过 IPC 传到主进程写临时文件、推理后即清理(详见 CLAUDE.md 中的音频管线描述:MediaRecorder → Blob → ArrayBuffer → IPC → File → whisper.cpp); - NVIDIA Parakeet:通过 sherpa-onnx 运行时做跨平台 ONNX 推理,相关管理逻辑在 src/helpers/parakeet.js 与 src/helpers/parakeetServer.js,模型存储在
~/.cache/openwhispr/parakeet-models/。
"不发送到外部服务器,除非用户显式配置"意味着:默认的本地模型模式下,语音内容不会离开本机;只有用户选择 BYOK(自带 OpenAI/Anthropic/Gemini 等密钥)或 OpenWhispr Cloud 模式后,音频才会按配置发往对应服务。这构成了隐私优先承诺的根基。
2. 凭据加密存储:safeStorage + 操作系统密钥链
这是 SECURITY.md 中最核心、也最值得深入的一段。文档的描述是:用户提供的 API 密钥(BYOK)与企业云凭据(AWS、Azure、Vertex)通过 Electron 的safeStorageAPI 加密落盘,safeStorage底层委托给操作系统密钥链(macOS Keychain、Windows DPAPI、Linux libsecret),密文存放在userData/secure-keys/下;非机密偏好(region、endpoint、hotkey、flag)继续存放在.env中;在缺少 keyring 的 Linux 系统上,密钥回退为明文(与 Electron 默认行为一致)。
仓库中的实际实现比文档描述更进一步,是一套AES-256-GCM 主密钥 + 系统密钥链的混合加密体系,核心代码在 src/helpers/secretCrypto.js:
const SERVICE = "OpenWhispr"; const ACCOUNT = "secrets-master-key"; const ALGO = "aes-256-gcm"; const IV_LEN = 12; const TAG_LEN = 16; const KEY_LEN = 32; const BACKUP_FILE = "master-key-backup.enc";其工作流程(_ensureInit)如下:
- 优先尝试 OS 密钥链(
_initKeychain):通过@napi-rs/keyring在系统密钥链中读写一个 32 字节(KEY_LEN = 32)的随机主密钥。若密钥链中尚不存在,则用crypto.randomBytes(32)生成并写入; - 写入 safeStorage 备份(
_saveMasterKeyBackup):主密钥首次生成时,用safeStorage.encryptString加密后写入userData/secure-keys/master-key-backup.enc,文件权限为0o600。注释明确指出只在首次生成时写入,避免每次启动都触发第二个 Keychain 后端; - 回退路径:密钥链不可用时,尝试从 safeStorage 备份恢复主密钥;仍不可用则回退到纯
safeStorage模式;若连 safeStorage 都不可用(无 keyring 的 Linux 环境),标记为unavailable——此时加密调用会抛出错误而非静默降级。
实际加密采用AES-256-GCM(带认证标签的认证加密):每条密文由IV(12B) || authTag(16B) || ciphertext拼接而成,GCM 同时保证机密性与完整性。解密时先按 GCM 结构解析,失败则回退尝试旧版 safeStorage blob(兼容迁移期数据)。
凭据的存取编排在 src/helpers/environment.js 的EnvironmentManager中:
SECRET_KEYS清单:包括全部 BYOK API 密钥(来自 src/config/secretKeys.js 的BYOK_API_KEYS清单,含 openai/anthropic/gemini/groq/xai/mistral/openrouter 等十余个 provider)加CORTI_CLIENT_ID/SECRET、BEDROCK_*、AZURE_OPENAI_API_KEY、VERTEX_API_KEY等企业凭据;- 按密钥单文件落盘:每个密钥独立加密为一个
<envVarName>.enc文件,位于userData/secure-keys/,写入采用"先写.tmp再rename"的原子写模式; - 启动加载:
init()时若无迁移哨兵文件.migrated则执行_migrateToSecureStorage()(将旧.env中的明文密钥加密迁移到 secure-keys,迁移后做往返验证,验证失败则保留明文.env不删除),随后_loadAllSecrets()把解密值注入process.env;解密失败时记录错误并提示用户重新输入; - 非机密偏好:
PERSISTED_KEYS中除密钥外的项(LOCAL_TRANSCRIPTION_PROVIDER、PARAKEET_MODEL、DICTATION_KEY、ACTIVATION_MODE等)继续以明文写入userData/.env,由saveAllKeysToEnvFile()统一维护——这与文档描述完全一致; - 密钥访问:渲染进程不能直接读文件,只能通过受限 IPC(
get-<base>-key/save-<base>-key)经 preload 桥访问(见 preload.js 的secretKeyApi,以及 src/config/secretKeys.js 注释中说明的test/helpers/secretKeys.test.js一致性守卫)。
值得一提的设计细节:渲染进程拿到的是解密后的明文密钥(内存中),而磁盘上只存在密文;Linux 无 keyring 时的明文回退是 ElectronsafeStorage的默认行为,属于已知的、文档明示的平台限制,而非 OpenWhispr 自身的疏漏。
3. 原生二进制:构建期从源码编译
Platform-specific helpers (key listeners, paste utilities) are compiled from source during the build process.
平台级辅助程序(按键监听、粘贴工具、麦克风监听等)的引入方式是安全设计的一部分:优先从源码编译,而不是下载来源不明的预编译二进制。仓库中可见的证据包括:
- macOS 的 Globe 键监听、麦克风监听由 Swift 源码编译(
scripts/build-macos-mic-listener.js等); - Windows 的按键监听器在 CI 中由 C 源码自动构建(
scripts/download-windows-key-listener.js下载的是由项目 CI 产出的、带版本 tag 的构建物); - 原生辅助代码位于 native/meeting-aec-helper(回声消除辅助),其
compat目录包含与系统头文件兼容的实现。
从源码编译/受控 CI 构建的最大收益是可审计、可复现:维护者与安全研究员可以审阅源码(如windows-key-listener.c使用 Windows 底层键盘钩子WH_KEYBOARD_LL),而不必信任某个匿名发布的二进制。同时,这些原生进程都按最小权限原则工作——例如按键监听只输出KEY_DOWN/KEY_UP事件,不触碰剪贴板内容以外的系统状态。
4. 上下文隔离与受限 preload 桥
The Electron renderer runs with context isolation enabled and a restricted preload bridge.
在 src/helpers/windowConfig.js 中可以看到三个窗口(主 dictation 窗口、Control Panel、通知窗口)的webPreferences配置:
// MAIN_WINDOW_CONFIG(dictation 主窗口) webPreferences: { preload: path.join(__dirname, "..", "..", "preload.js"), nodeIntegration: false, contextIsolation: true, sandbox: true, backgroundThrottling: false, },关键点解读:
contextIsolation: true:渲染进程与 preload 脚本、Electron 内部对象运行在隔离上下文,渲染进程即使被 XSS 攻破也无法直接访问 Node.js 能力或主进程 API;nodeIntegration: false:渲染进程没有 Node 集成,require、process等不可用;sandbox: true(主窗口/通知窗口):启用 Chromium 沙箱,进一步限制渲染进程的系统访问。Control Panel 因需要 preload 桥接跨域 API 调用而使用sandbox: false,但依然保持contextIsolation: true与nodeIntegration: false;- 受限 preload 桥:preload.js 通过
contextBridge.exposeInMainWorld("electronAPI", {...})只暴露白名单化的ipcRenderer.invoke包装函数,渲染进程不能任意调用ipcRenderer本身,IPC 通道是有限的、逐一声明的(get-<base>-key、save-<base>-key、db-*等),这直接回应了 Scope 中"不安全 IPC"这一攻击面。
contextIsolation与sandbox的叠加效果,加上 CLAUDE.md "Security Considerations" 一节中提到的"无远程代码执行、文件路径净化、受限 IPC 面",共同构成了针对 XSS 与 IPC 滥用两大威胁的纵深防御。
披露政策(Disclosure Policy)
OpenWhispr 采用**协调披露(coordinated disclosure)**流程:
- 安全研究员私下报告 → 维护者修复并发布版本 → 修复发布后公开细节;
- 修复发布后,报告者会在 CHANGELOG.md 中获得致谢(除非其选择匿名)。
这一流程的要点是"先修复、后公开",避免在补丁就绪前将漏洞细节公之于众,从而把窗口期内的被利用风险降到最低。对安全研究员而言,这意味着你的发现会在发布后被公开署名;对使用者而言,意味着及时升级到受支持版本(1.7.x+)即可获得已公开漏洞的修复。
结语:一份可执行的隐私与安全清单
OpenWhispr 的安全策略并非空泛承诺,而是一套与源码一一对应的工程实践。基于本文梳理,使用者可以形成自己的安全使用清单:
- 保持版本在 1.7.x 及以上,及时跟进 CHANGELOG.md 中的安全修复记录;
- 优先使用本地模型(whisper.cpp / Parakeet),语音默认不出本机;使用 BYOK 云模式时意识到音频会发送到对应服务商;
- API 密钥由系统密钥链保护(macOS Keychain / Windows DPAPI / Linux libsecret),密文位于
userData/secure-keys/;在无 keyring 的 Linux 上需知晓明文回退限制; - 发现安全问题时,通过私有渠道报告而非公开 Issue,并遵守协调披露流程。
对于希望进一步研究其实现的读者,建议从三条主线入手:凭据加密链路(src/helpers/secretCrypto.js → src/helpers/environment.js → src/config/secretKeys.js)、窗口隔离配置(src/helpers/windowConfig.js → preload.js),以及本地推理管线(src/helpers/whisper.js、src/helpers/parakeet.js)。
【免费下载链接】openwhisprVoice-to-text dictation app with local (Nvidia Parakeet/Whisper) and cloud models (BYOK). Privacy-first and available cross-platform.项目地址: https://gitcode.com/GitHub_Trending/op/openwhispr
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考