# 贴给 AI 之前的 30 秒：新手最容易漏掉的三样东西

> 贴给 AI 的日志里常常带着 .env 的值。这篇讲三步固定动作——只留能定位问题的最小集、把值换成占位符、贴前搜一遍，并附一个剪贴板一键脱敏的 zsh 函数：为什么只按 sk_live_、AKIA 这类前缀特征匹配，会让一串 32 位十六进制的密钥整行漏出去。

- 原文链接: https://laojin.blog/blog/20260901_what_not_to_paste_to_ai
- 作者: 老金
- 发布日期: 2026-09-01
- 标签: AI 安全, 数据脱敏, Claude Code, 开发习惯, 新手指南

---

![贴给 AI 之前的 30 秒：新手最容易漏掉的三样东西](https://pic-1258874139.cos.ap-hongkong.myqcloud.com/laojinblog/posts/20260901/00_cover.png)

我有个笔记仓库，几乎所有内容都会被 Claude 读到：工作日志、身份档案、正在做的项目、没想通的半成品。我不介意，那本来就是我写给自己看的东西。

但同一台机器上还有 App Store 的证书、几个 SaaS 的生产凭据。这些一行都不进对话框。

我一开始以为做这个区分需要点安全知识，后来发现不需要。谁都知道密码不能发出去。真正会出事的是那个动作：

报错了 → 终端 Cmd+A、Cmd+C → 粘贴 → 回车。

四步，两秒完成。而那段终端输出里，可能就带着你 `.env` 的值。

泄露的形状通常是这样：发出去的是日志，密钥恰好在日志里。

要做的事只有一个：在那两秒中间，插进一个 30 秒的固定动作。

---

## 漏点在动作上

大部分安全清单按"信息类型"来列：密钥、用户数据、公司代码。分类没错，但拦不住人——没人会主动去发密钥，出问题的是没意识到某个动作会把密钥一起带出去。

所以换个列法，按动作列：

| 你做的动作 | 顺手带出去的东西 |
|---|---|
| 报错了，整段终端复制 | `.env` 的值、数据库连接串、内部域名、本机绝对路径（里面有你的用户名） |
| 功能不对，把整个文件拖进对话 | 数据库表结构、内部接口地址、注释里留的测试账号 |
| 接口返回不对，把 response 原样贴 | 真实手机号、邮箱、`Authorization` 请求头 |
| 让 AI "读一下整个项目"、开仓库索引 | 一次性全带走，包括你忘了还在根目录的那个 `backup.sql` |

第四个最危险，因为它连"复制"这个动作都省了。你只说了一句"帮我看看这个项目哪里有问题"。

这四条都不需要判断力。它们的共同点是中间没有停顿。

![从终端到对话框，中间那道闸门一直是开着的](https://pic-1258874139.cos.ap-hongkong.myqcloud.com/laojinblog/posts/20260901/01_paste_gap.png)

## 留、改、扫

![留、改、扫三步流水线：先缩到最小集，再把值换成占位符，最后搜一遍](https://pic-1258874139.cos.ap-hongkong.myqcloud.com/laojinblog/posts/20260901/02_three_steps.png)

### 第一步 · 留：只给能定位问题的最小集

怕上下文不够，于是整个文件、整个日志一起贴上去——这是最常见的做法。其实 AI 要的是结构，具体的值它用不上。

| 该给 | 不用给 |
|---|---|
| 报错的那个函数本体 | 整个文件、整个仓库 |
| 错误栈里的关键几行 | 从启动到崩溃的全部日志 |
| 字段名和类型 | 字段的真实值 |
| 运行环境、复现步骤、预期与实际 | 生产环境地址和凭据 |

判断标准就一句话：**删掉它，AI 还能不能回答这个问题？** 能，就删掉。

### 第二步 · 改：把值换成占位符，格式留着

不要直接删字段——删了 AI 就看不出配置的结构关系了。要替换：

```
# 不要这样
POSTGRES_URL=postgres://tinypa:Xk9mZq2vLp@db-prod-sg.internal:5432/tinypa
APPSTORE_SHARED_SECRET=8f2c41ba9d7e4c1fa0b35e6d92c8a71b
HARMONY_CLIENT_SECRET=hm_sec_9K2mQx7ZpLd4RtV

# 这样
POSTGRES_URL=postgres://<DB_USER>:<DB_PASSWORD>@<DB_HOST>:5432/<DB_NAME>
APPSTORE_SHARED_SECRET=<APPSTORE_SHARED_SECRET>
HARMONY_CLIENT_SECRET=<HARMONY_CLIENT_SECRET>
```

`.env` 整个文件一键处理，一条命令：

```bash
sed -E 's/^([A-Za-z_][A-Za-z0-9_]*)=.*/\1=<VALUE>/' .env
```

输出长这样：

```
POSTGRES_URL=<VALUE>
APPSTORE_SHARED_SECRET=<VALUE>
DEBUG=<VALUE>
```

注意连 `DEBUG=true` 都被打掉了。这是故意的——宁可多打。你让 AI 看 `.env`，通常是想问"我是不是少配了一项""这个字段名对不对"，这两个问题都只需要键名。

日志和接口响应更麻烦，因为敏感值散在中间。我给自己写了个 zsh 函数，从剪贴板读、脱敏、再写回剪贴板，贴到 `~/.zshrc` 就能用：

```zsh
scrub() {
  pbpaste | sed -E \
    -e 's/("?[A-Za-z_]*(secret|key|token|password|passwd)[A-Za-z_]*"?[[:space:]]*[=:][[:space:]]*"?)[^[:space:]",}]+/\1<VALUE>/gI' \
    -e 's/(Bearer |bearer )[A-Za-z0-9._-]+/\1<TOKEN>/g' \
    -e 's/(sk|pk|rk)_(live|test)_[A-Za-z0-9]+/\1_\2_<APIKEY>/g' \
    -e 's/AKIA[0-9A-Z]{16}/<AWS_KEY>/g' \
    -e 's#(://[A-Za-z0-9._-]+):[^@/]+@#\1:<PASSWORD>@#g' \
    -e 's/[0-9]{17}[0-9Xx]/<IDCARD>/g' \
    -e 's/1[3-9][0-9]{9}/<PHONE>/g' \
    -e 's/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/<EMAIL>/g' \
    | pbcopy
  echo "已脱敏，可以粘贴了"
}
```

用法：复制日志 → 终端敲 `scrub` → 直接粘贴给 AI。

实测效果，输入：

```json
{"uid":"usr_7f3ad2","plan":"pro_monthly","bindPhone":"18700001234","notifyEmail":"freeman@tinypa.app","authorization":"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.pR0d-t0k3n_x"}
```

输出：

```json
{"uid":"usr_7f3ad2","plan":"pro_monthly","bindPhone":"<PHONE>","notifyEmail":"<EMAIL>","authorization":"Bearer <TOKEN>"}
```

三个说明，都是我实际踩出来的。

一是**第一条规则必须放在最前面，而且它是最重要的那条**。我最早的版本没有它，只按格式特征匹配（`sk_live_` 开头、`AKIA` 开头、`Bearer ` 后面），结果这一行完整地漏了出去：

```
APPSTORE_SHARED_SECRET=8f2c41ba9d7e4c1fa0b35e6d92c8a71b
```

它就是一串 32 位十六进制，没有任何前缀特征，正则认不出它和一个 commit sha 有什么区别。补上"字段名里带 secret / key / token / password 就把值打掉"这条，才抓住。代价是它比较粗：`APPSTORE_SHARED_SECRET=sk_live_xxx` 也会被整体打成 `<VALUE>`，看不出原来是 live 还是 test 环境。我认这个代价——漏一个密钥比丢一点上下文贵得多。

顺带说一句，这条规则不会误伤 `commit 42f3fb57...` 和 `userId`，因为它只看字段名，不看值长什么样。

二是**身份证那条必须排在手机号前面**。反过来的话，18 位身份证号中间会先被 `1[3-9][0-9]{9}` 吃掉 11 位，输出变成 `310101<PHONE>4` 这种半残状态，比不脱敏更难看出问题。我第一版就是这个结果。

三是**内部域名它管不了**。`db-prod-sg.internal` 这种得你自己改，正则没法替你判断哪个域名是内部的。

Linux 上把 `pbpaste`/`pbcopy` 换成 `xclip -o` / `xclip -selection clipboard`。

### 第三步 · 扫：贴之前搜一遍

习惯总有漏的时候，最后用工具兜一遍：

```bash
rg -n -i --hidden -g '!.git' \
  'secret|passwd|password|api[_-]?key|client[_-]?secret|shared[_-]?secret|token|cookie|authorization|BEGIN [A-Z ]*PRIVATE KEY' .
```

没装 `rg` 就用系统自带的：

```bash
grep -rniE 'secret|passwd|password|api[_-]?key|client[_-]?secret|token|cookie|authorization' .
```

如果只想检查本次改动：

```bash
git diff | grep -niE 'secret|password|token|api[_-]?key'
```

搜出结果不代表有问题，变量名叫 `token` 很正常。要看的是**等号右边有没有真值**。

## 打了码也可能还是认得出来

把手机号换成 `138****0000` 就算处理完了——这是最容易误判的地方。

看这段，一个人名都没有：

```
用户在 2026 年 8 月 20 日 18:32 下单，收货地址某市某区某街道 18 号，
金额 38,000 元，商品是某客户专属定制设备。
```

时间 + 地址 + 金额 + "专属定制"，四个信息叠起来，在公司内部足够定位到具体是哪个客户。单看每一项都不算隐私，组合起来就是。

![四条单看都不算隐私的信息，交汇处指向同一个具体对象](https://pic-1258874139.cos.ap-hongkong.myqcloud.com/laojinblog/posts/20260901/03_reidentification.png)

脱敏要达到的效果，是让数据没法指回某个具体对象，同时保住排查问题需要的结构。"看起来不像真数据"这个标准太松。三条具体做法：

- 删：跟当前问题无关的字段直接删，别留着"以防万一"。
- 降精度：精确时间 → "下单后约 3 分钟"；详细地址 → 城市级；真实金额 → 数量级相近的整数。
- 断关联：占位符之间不要留能对上的编号。三条日志都写 `<ORDER_ID>` 就行，别写 `<ORDER_1>` `<ORDER_2>`。

还有一个高频漏点：**只改了正文，忘了请求头**。手机号改了，`Authorization: Bearer ...` 还在下面躺着。

## 一个能直接抄的提问模板

线上接口报 500。不要这么问：

```
线上炸了，日志在这儿，你帮我看看：
[粘贴 800 行]
```

换成这样（照抄改字段就行）：

```
帮我查一个订阅状态接口的 500。

接口用途：客户端拉取当前订阅状态，服务端要先校验 App Store 收据
运行环境：Node.js 22 + Postgres 16
复现条件：同一张收据连续请求两次，第二次必然 500，第一次正常
预期结果：返回 { plan, expiresAt }
实际结果：500，响应体是空的

脱敏后的错误栈（密钥、Token、手机号已替换成占位符）：
[粘贴关键的那几行]

相关代码（只有报错的那个 handler 和它调的收据校验函数）：
[粘贴]

请先按可能性排序列出原因，再告诉我按什么顺序验证。
信息不够就直接说还缺什么，不要替我猜测配置和环境。
```

最后那句很重要。不加它，AI 会自己编一套配置往下推，你还得花时间分辨哪句是它猜的。

## 如果已经贴出去了

第一反应通常是删聊天记录。但删掉记录只是你自己看不见了，东西已经出去了。已经发出去的密钥，就当它已经公开。

正确顺序，第一步就是让它失效：

1. 立刻轮换或禁用这个密钥，先做这一步，别先查原因。
2. 查这个凭据的访问日志和账单，看有没有异常调用。
3. 把旧值从代码、日志、提交记录里清掉（`git log` 里也可能有）。
4. 按团队流程报备，不要自己捂着。
5. 新密钥配上去，顺手确认它没再进版本库。

## 顺手把工具设置确认一次

一次性检查，之后不用天天做：

- 现在登录的是个人账号还是公司账号？
- 对话内容会不会被用于改进模型，开关在哪？
- 编辑器插件的索引范围是当前文件还是整个 workspace？
- 团队到底允许不允许用这个工具处理源码？

把隐私开关全关严了，能贴什么仍然由公司制度和合规要求说了算。

## 贴墙清单

发之前扫一眼，全是勾就发：

- [ ] 没有密码、Token、Cookie、私钥
- [ ] 没有真实用户的手机号、邮箱、身份证、地址
- [ ] 没有生产环境的连接地址和内部域名
- [ ] 跟这个问题无关的内容已经删了
- [ ] 只改了正文的话，回头看一遍请求头
- [ ] 用的是符合团队规定的账号和工具
- [ ] 换成假数据之后，这个问题依然问得清楚

---

少贴一点、说得含糊一点，感觉上是更安全了，换来的是同样含糊的回答。问题没解决，下次只会更想把整个项目直接丢过去。

所以清单最后那条我用得最多：换成虚构数据还问得清楚吗。问不清楚，说明我自己还没想明白问题在哪，这时候贴多少上下文都没用。
