dsh-privacy-gateway
在宿主进程的 undici dispatcher 上拦截所有发往模型的请求:密钥、Cookie 与个人信息换成占位符,回复里再还原,一处拦截覆盖所有 provider(含插件自带的 adapter);按 kind 可选 mask / warn / block、可换成同形的假 key,记录按天落盘并带统计卡。
安装
dsh plugin --profile web add dsh-privacy-gateway
沙箱测试结果
| 阶段 | 结果 |
|---|---|
| 1 安装 | ✓ |
| 2 列装 | ✓ |
| 3 挂载 | ✓ |
| 4 启动 | — |
四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。
README
dsh-privacy-gateway
[English](README.en.md) | 简体中文
在宿主进程的 undici 全局 dispatcher 上装一层脱敏网关。出站把模型请求体里的密钥换成占位符,入站把回流里的占位符换回真值。对 agent、adapter、provider 都无感,一处拦截覆盖所有走 fetch() 的 provider,包括插件提供的 adapter。
如果你在用中转站或者在处理敏感信息、线上数据,请务必注意保护信息安全。
安装
# 正式版(npm) dsh plugin --profile web add dsh-privacy-gateway@latest # 同一版本的 GitHub Release 附件(预发布也发在这里) dsh plugin --profile web add https://github.com/xbzbing/dsh-privacy-gateway/releases/download/v0.1.4/dsh-privacy-gateway-0.1.4.tgz
预发布版本号带 -,只发 GitHub Release、不发 npm。仓库不提交 lib/,dsh plugin add github:… 拿到的树加载不了([ADR-0006](docs/adr/0006-distribution-and-build-artifacts.md))。
在默认的 web profile 里首次安装即装即用,无需重启 dsh 实例。通过 dsh web profile 的插件更新功能更新插件时,无需重启 dsh 实例。手动更新时建议先卸载旧版本再安装新版本,这样也不需要重启。
功能
- 脱敏范围:密钥、token、密码、私钥、cookie、邮箱、手机号、身份证、银行卡、自定义敏感词,各有开关。
- 动作可选:按 kind 选
mask/warn/block,密钥可换成同形的假 key(shape)。 - 记录与统计:按天落盘的脱敏记录、统计卡、会话「隐私保护」标签页;记录里只有占位符,没有真值。
/privacy命令:查状态、临时开关、test预览一段文本会怎样发出。- 配置热更新:日常项在插件页上改,下一次调用即生效;脱敏出错时请求不发出。
脱敏范围
kind · 内容 · 开关
API_KEY · OpenAI / Anthropic sk-…、GitHub / GitLab 令牌、Google AIza…、Slack xox…、Stripe、AWS AKIA…、阿里云 LTAI…、腾讯云 AKID…、Hugging Face、Groq、xAI、npm、PyPI、DigitalOcean、SendGrid 等前缀 · secrets
TOKEN · JWT · secrets
PRIVATE_KEY · PEM 私钥块 · secrets
PASSWORD / SECRET · URL 里的密码、名为 password / secret / token 的字段、中文「密码为…」「密钥是…」 · secrets
COOKIE · Cookie / Set-Cookie 头、curl -b、cookies.txt、Playwright storageState、HAR、常见会话 cookie 名(BDUSS、JSESSIONID 等) · secrets
EMAIL / PHONE / ID_CARD / BANK_CARD · 邮箱、大陆手机号、身份证号(校验位)、银行卡号(Luhn) · personal
TERM · 配置里 words 列出的词 · 有词就脱敏
脱敏过的值在请求的其它地方再出现,也按同一个占位符脱敏(finder 记为 known)。同一个 host 重发的历史保持原来的字节,prompt cache 照样命中。
照着试:下面每行都是假值,复制进会话发出去,统计卡的对应记录里就会出现替换后的占位符。记录、统计、日志和 vendorView 副本里都没有原值,只有占位符和首尾几个字符。
sk-proj-Zq7Lr2Vt9mK1Px3aB5cD8eF0gH4jN6wY → API_KEY ghp_Zq7Lr2Vt9mK1Px3aB5cD8eF0gH4jN6wYQ7RT2W → API_KEY AKIAQ7RT2W9KLM4NP8XZ → API_KEY Authorization: Bearer Zq7Lr2Vt9mK1Px3aB5cD8eF0gH4jN6wY9f2kLm4pQr7sT → TOKEN DB_PASSWORD=Zq7Lr2Vt9mK1Px3aB5cD8eF0gH4jN6wY9 → SECRET postgres://app:Zq7Lr2Vt9mK1Px3aB5cD8eF0gH4jN6wY9@db.internal/x → PASSWORD Cookie: BDUSS=Zq7Lr2Vt9mK1Px3aB5cD8eF0gH4jN6wY → COOKIE someone@gmail.com → EMAIL 13800138000 → PHONE 11010519491231002X → ID_CARD
- 中文写法「密码:…」「密钥是 …」和 URL 里的密码记为
{{PASSWORD_p7gqwtns}},和SECRET分开记。 EMAIL、PHONE、ID_CARD、BANK_CARD默认不换,打开「脱敏个人信息」才生效。- 「自定义敏感词」里的词按词替换,记为
TERM;「永不脱敏的值」和allow里的原样发出。 - 私钥整段一起换:
-----BEGIN到-----END,或只有开头一段 base64 的截断私钥。 - 文档域名(
example.com、.example)不算邮箱;手机号要 11 位、身份证要过校验位、银行卡要过 Luhn,否则不换。 - 字段的值是一个文件名时不是密钥:只由文件名的字符组成、且扩展名是已知的文件类型(模型权重
safetensors/gguf/ckpt等、压缩包、图片音视频、文档数据),原样发出([ADR-0026](docs/adr/0026-file-name-values.md))。 - 同一个值重复出现,占位符相同。
- 短于 8 个字符的秘密(
abc123、666666、888888这类口令)不算值:照原样发出,也不进记录。它们随时能被爆破,遮住只是给人一种并不存在的保护;值表本来也不把这么短的值当作已知值([ADR-0031](docs/adr/0031-no-short-or-template-values.md))。 - 明显是占位写法的值不算值:8 个以上相同字符连排(
sk-proj-xxxx…、AKIA0000…),或自带「这是占位」说明(your_api_key、example、changeme、placeholder、dummy、***)。随机 key 出现 8 连排的概率低于十亿分之一,sk_test_…这种真测试 key 照旧脱敏([ADR-0031](docs/adr/0031-no-short-or-template-values.md))。 - 加密算法的名字不算值:
SCRAM-SHA-512、PBKDF2WithHmacSHA256这类名字写在规范里、人人可读,遮住什么都不保护,原样发出([ADR-0033](docs/adr/0033-algorithm-names.md))。 - 键名和值一样脱敏:
{"metadata": {"sk-…": …}}里的键名也换成占位符,回复里再换回来;协议自身的键(model、role、type、name、id等)、工具定义和工具调用的参数名不动([ADR-0029](docs/adr/0029-mask-json-keys.md))。
界面
插件配置 · 会话「隐私保护」
 · 
配置
插件页面里本插件那一行的「配置」页上可以直接改日常项:enabled、secrets、personal、shape、toolGuard、maskKeys、words、allow、record、keepDays、recordContext、vendorView、showPanel、excludeProviders。改了即时保存,从下一次模型调用起生效,不用重启([ADR-0021](docs/adr/0021-live-settings.md))。其余项在 profile 的 ~/.dsh/profiles/<profile>/cordis.patch.yml 里改,写在 id: dsh-privacy-gateway 那一行的 config 下;插件包自带的 cordis.patch.yml 升级时会被覆盖,不要改它。
键 · 默认 · 说明
enabled · true · 总开关;关掉后网关仍在,但所有请求原样放行,不还原、不记录
secrets · true · 脱敏密钥、令牌、密码、私钥、cookie
personal · false · 脱敏邮箱、手机号、身份证号、银行卡号
words · [] · 自己的词(项目代号、客户名),按原样换成 TERM 占位符
allow · [] · 永不脱敏的值:整值匹配,不分大小写
actions · {} · 按 kind 选动作:mask(默认,换成占位符)、warn(照发并记一条)、block(整个请求不发)。例:{ PRIVATE_KEY: block, EMAIL: warn }([ADR-0013](docs/adr/0013-actions.md))
maskKeys · true · 读起来像密钥或个人信息的 JSON 键名也换成占位符({"sk-…": …});协议自身的键(model、role、type、name、id 等)、工具定义和工具调用的参数名永不动,否则供应商读不懂请求。关闭后这类键名原样发出([ADR-0029](docs/adr/0029-mask-json-keys.md))
toolGuard · off · 工具调用参数里还原出值、又指向本机以外的 host 时怎么办:off 只计数,warn 告警并记一条,block 这次调用的参数里不还原,工具拿到占位符([ADR-0014](docs/adr/0014-tool-guard.md))
shape · false · 密钥换成同形的假 key(前缀、长度、字符类别不变),而不是 {{…}} 占位符;不足 8 个字符、或含字母数字与 - _ . 以外字符的值仍用占位符;回复里照样还原([ADR-0015](docs/adr/0015-shape.md))
cookieNames · [] · 追加要脱敏的 cookie 名
cookieKeep · ['lang', 'locale', 'theme', 'tz'] · 永不脱敏的 cookie 名
protectEnvBlock · true · <env> … </env> 块原样发出,供会校验它的 provider 使用([ADR-0004](docs/adr/0004-protected-regions.md))
protectTools · true · 请求的 tools 数组原样发出
excludeHosts · [] · 不改写的 host 或 host:port
excludeProviders
…(truncated)
以上内容来自 npm 上的 README,我们只做格式化,不改内容。
版本
npm 上共发布:8,最后更新 2026-11-05