什么是 settings.local.json
Claude Code 从多个文件中读取设置。Anthropic 的设置文档将项目本地设置描述为 .claude/settings.local.json,用于“仅限你在此项目中使用”。其预期用途是针对单个项目的个人覆盖,以及在共享前的测试。
你不需要手动创建它。文档指出,Claude Code 会在“你第一次对权限提示给出长期授权”时写入 .claude/settings.local.json。权限文档详细说明了这对 shell 命令意味着什么。当你选择停止 Claude Code 再次询问并永久保存授权时,“例如对于 Bash 命令或 WebFetch 域名,Claude Code 会将规则保存到 git 代码库根目录下的 .claude/settings.local.json 中”。
同一页面中的两个细节至关重要。Bash 授权是“每个代码库和每个命令永久有效”的。并且该规则适用于“该代码库中任何位置的未来会话,包括在子目录和工作树(worktrees)中启动的会话”。你给出的一次授权会一直生效,并一直存在于文件中,直到有人将其删除。
为什么密钥会出现在文件中
Apiiro 的报告梳理了这一过程。“要求 Claude Code 检查数据库或调用 API,它建议的命令会携带来自你配置文件的密钥。它在运行前会进行询问。”密钥必须包含在命令中,因为没有它命令就无法运行。
接着是授权。Apiiro 写道,当你选择“不再询问”选项时,“它会运行命令并将权限规则添加到项目文件夹中的 .claude/settings.local.json,这样它就不会再问了。该规则就是命令字符串本身”。
权限规则旨在描述允许 Claude Code 运行的内容。当获得授权的命令中输入了密码、令牌或连接字符串时,规则会以明文形式存储该值。
Apiiro 表示这是工具的行为,而不是某个模型的怪癖:“这是 Claude Code 自身的行为,而不是模型的:它在 Anthropic 最新的模型上也会发生,我们在 Opus 5.5 和 Fable 5.1 上都复现了这一点。”
为什么 Claude Code 自身的保护并不总是有效
Claude Code 确实尝试将该文件排除在 git 之外。设置文档称,“Claude Code 第一次在尚未忽略该文件的 git 代码库中写入文件时,它会将 **/.claude/settings.local.json 添加到你的全局 git 排除文件中,因此该文件在每个代码库的提交中都会被排除。”
关键词是“全局”。该排除项存在于你机器上的 git 配置中,而不是代码库中。Apiiro 描述了这个漏洞:“这涵盖了常见情况。但它不涵盖后来变成代码库的文件夹、另一台机器上的克隆,或者某人已经提交的文件。”并且:“该规则从未进入代码库自身的 .gitignore,因此没有任何东西随项目移动来保护它。”
对于你自己创建的文件,还有一个记录在案的例外。设置表显示 Claude Code “在创建文件时将其排除在 git 之外;如果你手动创建它,请自行将其添加到 .gitignore”。
一旦文件被追踪,Claude Code 会改变处理方式,但仍会继续写入。权限文档称:“当文件在 git 中被追踪,或者 .claude 是一个符号链接时,Claude Code 会将其视为代码库提供的文件,并保留其规则,直到你信任该文件夹。”Apiiro 补充说,用户并没有被告知这一点,“并且下一次授权会被追加到被追踪的文件中。”
Apiiro 还追踪了是谁提交了该文件:“在我们追踪的 1,105 个包含真实密钥的代码库中,有 539 个是由 Claude 共同署名的提交修改了该文件。”其解释是 Claude Code 的提交指令列出了像 .env 和 credentials.json 这样的文件,但“它自己的设置文件不在该列表中”。
Apiiro 的发现
这些数据来自 2026 年 11 月 1 日至 3 日对默认分支进行的只读 GitHub 代码和提交搜索。Apiiro 表示未测试任何凭据:“没有使用或测试任何凭据,也没有联系任何主机。”
主要统计数据:
- 文件本身。 “在 GitHub 上的 104,185 份该文件公共副本中,有 1,122 份包含真实密钥。”
- 当前副本中的不同密钥。 “它们总共包含 1,379 个不同的密钥:636 个服务商密钥和 743 个公共主机的密码或令牌。”
- 数据库访问。 Apiiro 统计了 379 个数据库密码和 122 个 Supabase service-role 密钥,外加 103 个 Supabase 账户令牌。
- 服务器登录。 “43 个公共服务器的 SSH 凭据,其中一些是 sshpass 命令中的 root 密码。”
- 组织。 “63 个组织在公共允许列表中持有真实密钥。”Apiiro 表示大多数密钥属于个人开发者。
两项发现解释了为什么仅删除当前文件是不够的。首先,这些文件往往存在很长时间:“在包含真实密钥的代码库中,中位文件在被发现前 217 天就已首次提交。”其次,历史记录保留了它们:“旧版本的文件包含 734 个真实密钥,分布在 509 个代码库中,这些密钥在当前副本中已不存在,通常是因为文件已被删除。”
文件越大,包含的内容越多。Apiiro 写道:“超过 8 KB 的文件中,每十个就有一个包含真实密钥,而 500 字节以下的文件中,这一比例不到五百分之一。”
为什么密钥扫描器会漏掉很多密钥
你可能期望密钥扫描器能捕获这些。Apiiro 对每个文件运行了 gitleaks 和 TruffleHog。“在 1,122 个包含真实密钥的文件中,gitleaks 标记了 564 个,TruffleHog 标记了 643 个;有 215 个未被任何一个标记。”
原因是密钥的形态。扫描器寻找已知的密钥格式。而在 PGPASSWORD=、sshpass -p 或 mysql -p 之后输入的密码看起来就像普通命令行中的普通单词。Apiiro 的总结是:“缺口在于命令内部的密码。”
不仅仅是一个文件
这是研究中超出权限设置的部分。Apiiro 写道:“同样的‘命令中包含密码’模式出现在智能体记录其运行内容的任何地方:转录文本、memory 文件、交接笔记,而针对密钥格式调整的扫描器在所有这些文件中都会漏掉它。”
其列出的其他文件包括:
- 共享的
.claude/settings.json。 检查了所有 76,011 份公共副本,298 个文件包含真实密钥。团队是有意提交此文件的。 - 保存在代码库中的智能体聊天转录。 检查了 2,523 份,涉及 87 个代码库中的 120 个文件。
- 智能体 memory 文件 (MEMORY.md 及类似文件)。 检查了 1,200 份,涉及 9 个代码库。
- 交接和部署笔记。 检查了 2,095 份,涉及 33 个文件。Apiiro 补充道:“包含密钥的交接笔记中有三分之一是由智能体本身编写的。”
memory 文件的数量虽然较少,但模式是一样的。智能体记录的关于其操作的任何内容都可能携带命令中的任何信息。如果你在智能体维护的文件中保留项目上下文,这些文件应受到与设置文件相同的关注。
默认情况下,Claude Code 将会话转录存储在你的主目录 ~/.claude/projects 下。我们的 查找 Claude Code 会话历史 指南涵盖了它们的存放位置和保留时间。Apiiro 的转录统计涵盖了人们保存到代码库中的各种工具的导出内容。
在你的机器上也是如此
大多数副本从未到达 GitHub,Apiiro 认为这并不意味着它们是安全的。“允许列表在每个项目中都是相同路径下的纯 JSON,而且其中的大多数密钥就放在它们所开启的主机旁边。”它还指出,该文件可以在没有 git 的情况下流出,“在发布的 npm 包、使用 copy 命令构建的 Docker 镜像、zip 压缩包或同步文件夹中。”
如果你在一个代码库的多个检出(checkouts)中工作,该文件位于一个固定位置。文档称,在工作树中,Claude Code “使用主检出根目录下的文件”。我们的 Claude Code 工作树是否共享 memory 指南解释了工作树之间还共享哪些内容。
Anthropic 的回应
Apiiro 表示在发布前已与 Anthropic 分享了这项研究。在 11 月 8 日的更新中,Apiiro 报告称 Claude Code 的负责人于 11 月 7 日在 Hacker News 上做出了回应。
根据 Apiiro 的更新,他写道团队正在“研究我们可以做些什么来防止这种情况”,并列出了计划进行原型设计的方案,“确保自动模式能捕获此类情况,内置基础密钥扫描”。Apiiro 还报告称,在他看来,暴露的副本是手动创建并签入的,他建议使用自动模式而非权限规则,并建议使用 GitHub 和其他托管商提供的密钥扫描功能。
截至 2026 年 11 月 10 日发布的 Claude Code 版本,发行说明中尚未描述授权保存方式的变更。请将此视为一个未解决的问题,并在依赖任何修复程序之前检查变更日志。
人们首先尝试的方法
删除文件并提交删除。 这删除了当前副本。但它不会删除历史记录、分支或克隆,而 Apiiro 正是在这些地方发现了 734 个在当前副本中不存在的密钥。
假设全局排除保护了每台机器。 它只保护了 Claude Code 写入它的那台机器。新的克隆、CI 运行器或后来变成代码库的文件夹并没有这个排除项。
仅依赖密钥扫描器。 Apiiro 测试的两个扫描器都漏掉了很大比例的文件,因为命令内部的密码看起来不像密钥。
一次又一次地授权相同的长命令。 每次长期授权都会添加一条字面规则,Apiiro 发现较大的文件包含密钥的可能性要大得多。
修复方案:检查、轮换,然后清理
第 1 步:查找所有副本
在你的 GitHub 组织中搜索 .claude/settings.local.json 和 .claude/settings.json。然后检查每个代码库的 git 历史记录中是否存在该文件,即使它现在已经消失。在你自己的机器上,找到每个副本并阅读其允许列表。在 Claude Code 内部,/permissions 会列出你的规则以及每条规则来源的设置文件。
第 2 步:先轮换,后删除
Apiiro 的顺序是“先轮换,后清理”。任何曾经出现在文件中(无论在哪个提交中)的凭据都应被视为已泄露。删除文件并不能清除历史记录、分支或克隆。
第 3 步:取消文件追踪并保护代码库
如果文件已被追踪,Apiiro 建议使用 git rm --cached .claude/settings.local.json 取消追踪,并将其添加到代码库自身的 .gitignore 中。这样,保护措施就会随项目移动,而不仅仅留在你的机器上。
第 4 步:改变授权方式
删除所有包含密钥的规则,即使是在从未提交的文件中。然后停止在命令中输入密钥。通过变量名引用它们,这样规则记录的是名称而不是值。授权模式(patterns)而不是字面命令:文档中的示例 Bash(npm run *) 可以匹配 npm run build 和 npm run test --watch。要注意模式的宽泛程度,因为文档指出第一个 * 之前的所有内容才是限制规则的关键。
第 5 步:将智能体文件视为敏感文件
将 .claude/、保存的转录文本、交接笔记和智能体 memory 文件添加到你的 pre-commit 和 CI 密钥扫描中,包括历史记录。确保扫描器会检查命令内部的密码,而不仅仅是密钥格式。
在 MemoryLake 中设置
Apiiro 的发现归结为一个习惯:智能体记录了它所做的事情,而你输入到命令中的任何内容都会随之记录。设置文件、转录文本和 memory 文件都会记录工作。在其中的每一个文件中,命令中的任何内容都可能与工作一起被记录下来。
MemoryLake 是一个 memory 层,用于存放你希望助手拥有的项目上下文,例如服务使用哪个数据库、暂存环境在哪里以及团队运行哪些命令,可供你连接的助手(如 ChatGPT、Claude Code 和 Codex)使用。
你用自己的语言亲自编写条目。不会从你的 .claude 文件夹、设置文件、会话转录、自动 memory、代码库或任何供应商的存储中读取、写入或删除任何内容。Claude Code 的权限规则按 Anthropic 描述的方式工作,你仍然需要按上述方法清理设置文件。
第 1 步:创建 API 密钥
登录并在仪表板中生成密钥。像存储任何其他凭据一样将其存储在代码库之外。

第 2 步:上传你的第一批 memory
编写上下文,而不是凭据:脚本预期的环境变量名称、服务运行的主机、团队轮换密钥的步骤。每个条目一个事实,严禁包含密码或令牌。

第 3 步:连接你的 AI 和智能体
连接 Claude Code 和你使用的任何其他助手。它们可以利用相同的书面上下文,而密钥则保留在你的密钥管理器中。

这在实践中改变了什么
第一个区别是上下文和凭据之间有了清晰的界限。项目知识存在于你编写的条目中,而开启系统的数值则存在于专门为它们构建的地方。
第二个区别是减少了将会话中粘贴密钥的理由。当助手已经知道哪个变量持有连接字符串时,命令就可以直接引用该变量。
Claude Code 权限的最佳实践
在授权前阅读命令。 尤其是当它连接到数据库或服务器时。
授权模式,而非字面命令。 像 Bash(npm run *) 这样的规则不会携带你输入过一次的参数。
将密钥保留在变量中。 这样规则记录的就是变量名。
将文件添加到代码库自身的 .gitignore。 不要依赖只有你机器才有的全局排除。
定期审查允许列表。 /permissions 显示每条规则及其来源。
扫描智能体文件和历史记录。 设置、转录文本、交接笔记和 memory 文件都可能携带命令。
有疑虑即轮换。 历史记录和分支会保留你删除的内容。
结论
当你选择停止 Claude Code 询问 shell 命令的选项时,Claude Code 会将该命令作为允许规则保存在 .claude/settings.local.json 中。如果命令包含密码或令牌,规则也会包含它。Claude Code 在创建该文件时会将其添加到你的全局 git 排除项中,但这种保护不会随代码库移动。Apiiro 发现了 1,122 份包含真实密钥的该文件公共副本,并在其检查的所有智能体文件中发现了 2,539 个不同的密钥。
检查你的代码库和历史记录中是否存在该文件,轮换它曾经持有过的任何内容,取消追踪并将其添加到代码库的 .gitignore 中,并改变你授权命令的方式。然后,将智能体编写的每个文件(包括 memory 文件)都视为可能携带密钥的文件。