Claude Code 用 eval 包装命令,数据库密码在 ps 中全程可见
V2EX 用户 mysoko 检查自己 253 个 Claude Code 会话记录,发现 16 个包含明文密码。复现表明:Claude Code 将命令包在 source 快照加 eval 的复合结构中,父 shell 进程全程保留完整命令行,即使 curl 自身已抹掉子进程中的密码。
V2EX 用户 mysoko 在自己机器上翻了一遍 Claude Code 的会话记录:253 个 jsonl 文件里,有 16 个出现过 mysql -p 密码、curl -u user:pass 这类写法。他顺手用 Claude Code 2.1.317 跑了一条带假密码的 curl 命令,另起进程抓 ps 输出,发现暴露面比自己以为的多了一层。
ps 里出现两个进程。下面一行是 curl 自己,密码那几位已经被它抹成空格——curl 确实做了这件事。上面一行是 Claude Code 起的 zsh,整条命令连密码都在,curl 跑多久它就挂多久。
这个差异来自执行方式的不同。如果自己在终端敲 bash -c 'curl ...' 只跑一条命令,bash 会直接 exec 成 curl,看不到中间那层。但 Claude Code 把命令包在 source 快照 && ... && eval '你的命令' 里,这是复合命令,shell 必须留着等它跑完,所以这份完整命令行一直在进程表里。
除了 ps,命令还会原样写进 ~/.claude/projects/ 下面的 jsonl 会话记录。在服务器上更要留意:Linux 默认挂载参数下,proc(5) 写明 everybody may access all /proc/pid directories,同机其他用户能看到你的命令行。环境变量反而不一样,读 /proc/pid/environ 要过 ptrace 的权限检查。共用开发机或跳板机的用户需要注意这个区别。
mysoko 给了一段自查正则,只数命中数量不打印内容。他自己的结果是会话 16 个、shell 历史 8 行。他明确说正则比较粗,会漏也会误报,不是 0 就值得翻一下。不装额外工具的改法是:~/.my.cnf 和 ~/.pgpass 各自配好并 chmod 600,在 CLAUDE.md 或 AGENTS.md 里加一句密钥不要出现在命令参数里、用客户端自己的配置文件。已经进过会话的密码直接换掉,删 jsonl 没用,那段已经发给模型了。
他目前用自己写的小工具 rootkey 做了一层隔离:让 AI 跑 rootkey run "订单库" --env-var PGPASSWORD -- psql ...,值只进那个子进程的环境变量,ps 和会话记录里只有条目名。但他也承认这不是银弹:MySQL 官方更推荐的还是 600 权限的配置文件;同一个用户下跑的恶意程序照样能读环境变量和文件。