跳转到内容

TCC 隐私授权 ​

「某某想要访问你的桌面文件夹」——这类弹窗背后是一套独立于 Unix 权限的机制。界面上它叫「隐私与安全性」,系统内部的技术名称是 TCC(Transparency, Consent and Control)。

它和上一篇讲的权限位完全是两回事:即使文件属于你、权限位也允许,TCC 仍然可以拦下访问。搞清它按什么记录授权,能解释大部分「脚本在终端里能跑、放进后台就失败」的问题。

开始之前 ​

需要读过权限与扩展属性,尤其是 Permission denied 与 Operation not permitted 的区别。本文的修改操作只有一个(重置授权),影响范围有限且可恢复。

授权按「应用 + 类别」记录 ​

打开「系统设置 → 隐私与安全性」,左边是类别,点进去是获得该类别授权的应用列表。常见类别:

类别涵盖什么
辅助功能控制其他应用、模拟按键与鼠标操作
输入监控读取键盘输入事件
完全磁盘访问权限访问邮件、信息、时间机器备份等受保护位置
屏幕录制抓取屏幕内容
文件与文件夹桌面、文稿、下载等目录的逐项授权
自动化用 Apple 事件控制另一个指定应用

类别的名称和分组会随 macOS 版本变化,本文以 macOS 15 及以后的界面为准,其他版本以你机器上的实际项目为准;基础用法见隐私与安全性。

「自动化」这一项和别的不同:它记录的是一对关系——谁控制谁。终端控制访达、终端控制备忘录是两条独立记录,见 AppleScript 与 JXA。

脚本的授权不属于脚本 ​

这是最容易卡住的一点。

你写了一个读取 ~/Desktop 的脚本,在终端里执行。系统弹窗时问的不是「你的脚本想访问桌面」,而是「终端想访问桌面」。因为 TCC 追究的是负责的那个进程——它顺着进程的父子关系往上找到真正代表用户发起操作的应用,也就是终端。

由此推出三条实用结论:

  1. 换一个终端应用,授权要重来。 在「终端」里授权过,改用 iTerm2 或 Ghostty 后会再问一次。
  2. 给终端授予完全磁盘访问权限,等于给了它能运行的所有命令。 这是一次范围相当大的授权,值得单独想清楚再点。
  3. 从 launchd 运行时没有人可以点「允许」。 后台任务首次碰到受保护数据时,请求要么静默失败,要么弹窗出现在没人看的时候。所以launchd 一文强调:先在终端手工跑一次完成授权,再交给后台。

为什么授权会突然失效 ​

TCC 记录和应用的代码签名绑定。出现下面几种情况时,系统认为这不再是当初你授权的那个程序:

  • 应用更新后签名变了(正规更新通常能保持,但并非总是)。
  • 应用被移动、替换或用非官方方式打了补丁。
  • macOS 大版本升级后重置了部分类别的授予。

表现往往是静默失败:程序不报错,只是什么都没发生。Hammerspoon 失去辅助功能权限后窗口操作毫无反应,就是这种情况。所以排查自动化问题的第一步应当是回到隐私设置确认那一条还在、开关还开着。

用 tccutil 重置一次授权 ​

想让弹窗重新出现——例如你误点了「不允许」,或者想验证首次运行的体验——用系统自带的 tccutil:

sh
tccutil reset All com.apple.Terminal

reset 是目前唯一支持的命令。第一个参数是服务名,第二个参数(可选)是应用的 bundle ID;给了 bundle ID 就只重置那个应用,不给则重置该服务下所有应用的决定。

只重置某一类:

sh
tccutil reset Camera com.apple.Terminal

常见服务名包括 All、Accessibility、Camera、Microphone、ScreenCapture、AddressBook、Calendar、Reminders、Photos、AppleEvents、SystemPolicyAllFiles(对应完全磁盘访问权限)。man tccutil 里只给了 AddressBook 和 All 两个例子,并没有完整列表,其余服务名来自实践;写错服务名时命令会报错,不会误伤别的类别。

查询某个应用的 bundle ID:

sh
osascript -e 'id of app "Terminal"'

重置会做什么、不会做什么。 它清除的是「同意」和「拒绝」这两类决定,让下次访问重新弹窗。它不会删除你的数据,也不会改变系统设置里的其他项目。撤销方式就是重新授权一次。

不要去直接改 TCC 的数据库文件。那个文件受系统完整性保护,正常情况下改不动;能改动它的前提是你降低了系统保护级别,代价远大于收益。

遇到「Operation not permitted」怎么办 ​

按这个顺序判断:

  1. 确认报错的是 Operation not permitted 而不是 Permission denied。后者是权限位问题,与 TCC 无关。
  2. 想清楚被访问的是哪类数据——桌面/文稿/下载属于「文件与文件夹」,邮件和备份属于「完全磁盘访问权限」。
  3. 在隐私设置对应类别里,检查你正在使用的终端应用是否在列表中且开关打开。
  4. 打开开关后完全退出并重新启动终端。TCC 的变更对已经运行的进程不生效。

第 4 步经常被跳过,然后得出「授权了也没用」的结论。

检查结果 ​

sh
ls ~/Library/Mail

终端没有完全磁盘访问权限时返回 Operation not permitted;授权并重启终端后能列出内容(或提示目录不存在——没用过邮件 App 就没这个目录,同样说明拦截已解除)。

sh
tccutil reset AddressBook

执行后,下次有应用访问通讯录时会重新弹窗。这是本文唯一会改变状态的命令,影响范围仅限通讯录类别。

动手练习 ​

用一个低风险的类别走完整个循环:

  1. 执行 osascript -e 'id of app "Terminal"',记下 bundle ID。
  2. 在终端执行 osascript -e 'tell application "System Events" to get name of first process',在弹窗里点允许。
  3. 到「系统设置 → 隐私与安全性 → 自动化」,找到刚生成的那条记录。
  4. 执行 tccutil reset AppleEvents,回到设置里确认记录消失了。
  5. 重新执行第 2 步,确认弹窗又出现了。

成功标准是第 4、5 步——你能主动让一次授权决定回到初始状态。这在测试「新用户第一次运行会看到什么」时非常有用,也是遇到误点「不允许」时的正规解法,不需要重装应用。

返回系统内部