敲敲敲。谁?Touch ID。又来了。
想象一下:你正在终端工作,用op read查询1Password的密钥。需要Linear的API密钥。Touch ID。OpenRouter的。Touch ID。Gitea的。Touch ID。
半小时内它要求我验证指纹十四次。
你知道当一个安全工具在三十分钟内打断你十四次会发生什么吗?第五次时你就不再看它在要求什么了。你下意识地放上手指。“是的,随便什么,让我工作吧。”
而这正是安全性完全崩溃的地方。
授权疲劳:没人愿意正视的问题
这在安全领域有个名词:授权疲劳。这不是什么新概念。这与MFA疲劳攻击使用的原理相同:用授权请求轰炸用户,直到他们纯粹因为疲惫而接受一个。
2022年,一个17岁的孩子正是这样进入Uber内部系统的。他反复向员工发送认证推送通知,深夜时分,直到那个人为了能睡觉而接受了一个。
显然,1Password要求Touch ID不是攻击。但心理效应是相同的:它训练你不假思索地批准。
这就像那些多年来出现在每个网站上的cookie横幅。一开始你会阅读它们。现在你不看就点击"全部接受"。恭喜:一个设计用来保护你隐私的机制教会了你更快地放弃你的隐私。
为什么1Password每次都要我的手指
我的设置:我使用op read从终端读取1Password的密钥。运行得很好。问题是我使用Claude Code(一个终端AI助手),它执行的每个命令都是一个新进程。
1Password的生物识别会话超时是10分钟不活动,并在每次使用时刷新。理论上,不应该这么频繁地要求手指。但Claude Code不重用进程:每次需要密钥时,它启动一个新的shell,1Password将其解释为新会话。
结果:每次Claude需要密钥时都要Touch ID。这是持续性的。
解决方案:40行缓存
想法很简单:一个包装器在PATH中排在op前面。当你执行op read时,它检查是否已经缓存了新鲜的结果。如果有,直接返回而不接触1Password。如果没有,调用真正的op,缓存结果,完成。
对于任何其他子命令(op signin、op item list等),直接传递给真正的op而不干预。
| |
将它保存在~/.local/bin/op,给予执行权限,由于~/.local/bin在PATH中排在/opt/homebrew/bin之前,你的包装器会拦截调用。
安全决策
让我们诚实面对我们在做什么:在磁盘上以明文保存密钥。这听起来很糟糕。但让我们将其放在上下文中。
缓存什么:
- 仅
op read的结果(个别密钥的读取) - 其他所有内容直接传递给真正的
op
保存在哪里:
~/.cache/op-cache/,权限700(仅你的用户)- 每个缓存文件权限
600(仅你可读/写)
持续多长时间:
- 默认1小时,可用
OP_CACHE_TTL配置
文件名:
- 是参数的SHA-256哈希,不会泄露包含哪个密钥
比.env.local更危险吗? 不。完全一样。你的.env.local文件也是磁盘上具有限制性权限的明文密钥。而你在每个项目中都有这些。
比1Password已经做的更危险吗? 1Password应用在解锁时将你的vault保持在内存中解密状态。我们的缓存更受限(仅你读取过的密钥,不是整个vault)但不够复杂(磁盘 vs 内存)。
我们担心的事情(但不知道如何解决)
这是诚实的部分。我们不是安全专家。我们做出了看起来合理的决策,但可能遗漏了什么。一些疑虑:
1. 锁屏时应该清除缓存吗? 现在,如果你锁定Mac而有人访问磁盘(盗窃、evil maid),缓存的密钥就在那里。虽然如果有人能访问你的磁盘,你可能已经有更大的问题了(FileVault应该防止这种情况)。
2. 有竞态条件吗?
如果两个进程同时对同一个密钥执行op read,两者都可能尝试同时写入缓存。实际上不应该造成严重问题(最坏情况是部分读取),但这不优雅。
3. 哈希足够吗?
我们使用参数的SHA-256作为文件名。如果有人能访问~/.cache/op-cache/,他们无法知道每个文件中有什么密钥,但可以读取所有内容。权限600应该阻止这一点,但如果有受损进程以你的用户身份运行…
可以改进的地方
一些我们尚未实现的想法(暂时):
- 过期文件的自动清理(用
cron或launchd定期清除) - 使用会话派生密钥的缓存加密
- 从缓存提供密钥时的通知(在stderr中显示"(cached)")
- 使用
op cache clear或类似命令的手动失效
这里需要你的参与
听着,我写这篇文章时诚实承认自己不是安全专家。我们做出了看似明智的决策。威胁模型很清楚:保护我们免受授权疲劳而不打开明显的漏洞。
但"看似明智"和"安全"是两回事。
如果你比我们更了解安全(这不难)并且看到明显的缺陷、我们没有考虑的边缘情况,或者简单地说有更好的方法来做这件事:告诉我。真的。评论开放,我的邮箱也是。
我宁愿有人告诉我"你做的是危险的临时方案",也不愿在为时已晚时才发现。
房间里的大象
1Password应该原生解决这个问题吗?是的,可能应该。每个应用的可配置超时,或者在定义期间保持授权活跃的"工作会话"模式,将消除这个包装器的需要。
但在他们没有这样做的时候,替代方案更糟:继续每30秒放一次手指,直到你的大脑断开连接,开始不看就批准。
因为这就是过度安全的悖论:如果工具太烦人,你最终会比不使用它时更不安全。至少没有它时你意识到自己没有保护。有授权疲劳时,你以为自己受到保护,却闭着眼睛批准任何东西。
世界上最好的锁如果主人因为厌倦找钥匙而让门开着,就毫无用处。
相关: 如果你对为什么我们将所有密钥集中在1Password感兴趣,请阅读GitHub泄露的3900万个密钥。如果你想看看当你给AI太多能力时会发生什么(剧透:它发送44封虚假邮件),当你的AI变成你最大的敌人是恐怖故事。