问题现象
从 GitHub Releases 下载并安装 Deskflow v1.26.0 或 InputLeap 后,双击打开时 macOS 弹出两个不同的错误提示:
情况一 — “已损坏”:
“Deskflow” is damaged and can’t be opened. You should move it to the Trash.
情况二 — “无法验证”:
“InputLeap” cannot be opened because the developer cannot be verified. macOS cannot verify that this app is free of malware.
这两个提示看起来吓人,很多人第一反应是下载的文件损坏了,或者是木马病毒。但实际上 这是 macOS Gatekeeper 的安全机制在起作用,与文件完整性无关。
本文从技术底层分析这两个提示的区别、产生原因,并提供修复方法。
一、macOS 应用安全模型:Gatekeeper 与代码签名
要理解这个问题,需要先了解 macOS 的三个安全层级:
1.1 Quarantine(隔离标记)
当你通过浏览器、下载管理器(如 Free Download Manager)、AirDrop 或 iMessage 获取应用时,macOS 会自动给文件加上一个扩展属性:
1 | com.apple.quarantine |
这个属性存储了下载来源的元数据:
1 | $ xattr -l /Applications/Deskflow.app |
格式为:<flags>;<timestamp>;<来源应用>;<UUID>。有了这个标记,macOS 知道这个应用是从外部下载的,需要在首次启动时进行安全检查。
1.2 Code Signing(代码签名)
所有 macOS 应用都应该有数字签名。签名有两种方式:
| 签名类型 | 含义 | 典型用途 |
|---|---|---|
| Development(开发签名) | 用本地生成的证书签名 | Xcode 调试,仅本机可用 |
| Developer ID(分发签名) | 用 Apple 签发的开发者证书签名 | 公开发布,需要 Apple 公证 |
通过 codesign 命令可以查看签名信息:
1 | $ codesign -dvvv /Applications/Deskflow.app |
1.3 Notarization(公证)
即使应用有 Developer ID 签名,macOS 还会在后台将应用的哈希值发送给 Apple 服务器验证是否包含已知恶意软件——这个过程叫公证(notarization)。公证成功的应用会得到一个盖章(ticket),嵌入到应用中。
二、为什么 Deskflow 和 InputLeap 会报错
2.1 共同根因:Adhoc 签名 + Quarantine 标记
这两个开源项目都是社区贡献者用 adhoc(自签名) 方式打包的。所谓 adhoc 签名,就是在编译时由链接器(linker)打上的一个本地签名:
1 | # Deskflow 的签名 |
linker-signed 意味着连手动 codesign 命令都没有执行,完全依赖编译器自动签名。
当应用同时满足以下两个条件时,macOS 就会拒绝启动:
- 有
com.apple.quarantine属性(从外部下载) - 代码签名不是 Apple Developer ID(无法联系公证服务)
运行逻辑是这样的:
1 | 用户双击 → 发现 quarantine 标记 → 触发 Gatekeeper 验证 |
2.2 两个错误提示的细微区别
虽然根因相同,但 Deskflow 和 InputLeap 的签名质量不同,导致 macOS 给出不同等级的警告:
| 指标 | Deskflow | InputLeap |
|---|---|---|
| 签名类型 | adhoc |
adhoc, linker-signed |
| Sealed Resources | 有(33个文件) | 无 |
| Info.plist 签名 | 已绑定 | 未绑定 |
| macOS 提示 | “is damaged” | “cannot be verified” |
“Damaged” 是更严厉的提示——macOS 认为应用内容(签名摘要与文件不匹配或未被签名覆盖)可能已被篡改。InputLeap 连 Info.plist 都没有签名覆盖,意味着理论上你可以随意修改它的 Info.plist 而不会被检测到。
2.3 为什么开源项目不解决这个问题
这背后有一个现实原因:Apple Developer Program 年费 $99。
很多开源项目没有这笔预算,或者维护者不愿意每年支付这笔费用只为给 macOS 用户一个更好的体验。即使支付了,还需要配置 CI/CD 流水线做公证(notarization),进一步增加了维护负担。
以 Deskflow 为例,GitHub 上有 相关 issue 讨论 macOS 签名问题,但因为流程复杂和费用问题,至今没有解决。
三、排查方法:一步一步验证
如果你遇到类似问题,可以用以下命令诊断:
步骤 1:检查是否有 Quarantine 标记
1 | xattr -l /Applications/YourApp.app |
如果输出包含 com.apple.quarantine,说明隔离标记已设置。
步骤 2:检查代码签名类型
1 | codesign -dvvv /Applications/YourApp.app 2>&1 | grep -E "Signature|TeamIdentifier|flags" |
Signature=adhoc→ 自签名,大概率是问题所在Signature=Developer ID Application→ 有正确的分发签名TeamIdentifier=not set→ 无开发者团队
步骤 3:查看 Gatekeeper 评估结果
1 | spctl --assess --verbose /Applications/YourApp.app |
输出 accepted 表示通过(可能因为安全策略已关闭),rejected 表示被拦截。
步骤 4:检查二进制架构
1 | file /Applications/YourApp.app/Contents/MacOS/* |
确认是 arm64(Apple Silicon)还是 x86_64(Intel)。如果你的 Mac 是 Apple Silicon 但下载的是 Intel 版本,需要通过 Rosetta 运行,但一般不会导致 “damaged” 错误。
步骤 5:检查动态库链接
1 | otool -L /Applications/YourApp.app/Contents/MacOS/YourApp |
确认所有链接库都能找到。缺失的动态库也会导致启动失败,但错误提示不同(通常是”意外退出”而不是”已损坏”)。
四、修复方法
方法一:移除 Quarantine 属性(推荐)
1 | sudo xattr -rd com.apple.quarantine /Applications/YourApp.app |
参数说明:
-r:递归处理,移除所有子文件和目录的属性-d:删除指定的扩展属性
执行后,macOS 就不再对该应用进行启动安全检查,可以直接打开。
方法二:通过右键菜单打开
在 Finder 中找到应用,按住 Control 键点击(或右键),选择 “打开”。在弹出的对话框中点击 “打开”,macOS 会记住这次授权,后续可以直接双击打开。
但这个方法治标不治本,下次更新后又会弹出提示。
方法三:关闭 Gatekeeper(不推荐)
1 | sudo spctl --master-disable |
系统偏好设置中会出现”任何来源”选项。这是最彻底的方案,但会降低系统安全性,不建议日常使用。
五、预防:如何避免再次遇到
- 优先从 Mac App Store 安装:经过苹果审核签名,不会有此问题
- 通过 Homebrew 安装:
brew install deskflow——Homebrew 会处理签名和权限问题 - 关注项目的官方 DMG 签名:如果项目发布带 Developer ID 签名的版本,优先选择
- 下载后先移除隔离属性:养成习惯,下载安装后立即执行
sudo xattr -rd com.apple.quarantine /Applications/xxx.app
总结
| 问题 | 原因 | 解决 |
|---|---|---|
| “已损坏,移到废纸篓” | Adhoc 签名 + Quarantine 标记导致 Gatekeeper 拒绝 | sudo xattr -rd com.apple.quarantine |
| “无法验证开发者” | Linker-signed 签名 + 无公证 + Quarantine | 同上 |
| “意外退出” | 架构不匹配 / 缺少动态库 | file + otool -L 排查 |
对于 macOS 上的开源应用,”is damaged” 和 “cannot be verified” 最常见的真实原因不是文件损坏,而是没有正确的 Apple Developer ID 签名。通过移除 quarantine 属性即可解决,无需重装、无需重新下载。
参考资料
- Apple Developer Documentation: Code Signing
- Apple Support: Open a Mac app from an unidentified developer
- Deskflow GitHub Releases
man codesign/man xattr/man spctl