本地 DPAPI 免口令凭据库(Windows)
何时用
- 需要在本机安全保管一把密钥/口令(如云 API 密钥对、token),且信任边界就是"当前 Windows 登录用户"。
- 想要免口令体验:DPAPI 由系统用当前用户凭据透明加解密,无需记忆主口令。
- 不想把明文写进任何文件/日志/云文档;落盘只有 DPAPI 密文 + base64 封装。
- 需要给凭据打泄漏标注(如"已在聊天明文暴露、待轮换"),供后续取用前警告。
何时不用
- 跨设备/跨账号同步 → 用
unified-credential-vault(带主口令、可恢复、全生态同读)。 - 需要多人共享或换机可解 → DPAPI 绑定本机本用户,换机/换账号解不开。
- 极高密级且担心"同账号被拖" → DPAPI 仅防其他用户/其他机,同账号可读,应上主口令保险库。
用法
默认保险库目录:~/.ucvault_local(即 C:\Users\<你>\.ucvault_local)。
推荐入口:key_drop.py(密值不进命令行、不进历史)
~/.ucvault_local/tools/key_drop.py。三种模式,都不把密值写进命令行参数:
# 1) 单值,隐藏输入(Git Bash / 支持 tty 的终端)
python ~/.ucvault_local/tools/key_drop.py hunyuan_api_key
# 2) 成对密钥对(如云 SecretId + SecretKey)
python ~/.ucvault_local/tools/key_drop.py tencent_cos --pair
# 3) 记事本模式(最保险,任何终端都能用,密值走 GUI 不经过终端)
python ~/.ucvault_local/tools/key_drop.py tencent_cos --pair --notepad
- 模式 3 是最稳的:不依赖终端能力,密值只经过记事本,导入后临时文件立即删除。 终端不支持隐藏输入、或旁边有人/在录屏时,一律用它。
vault put <label>(vault.py 自带)只能存单值,存不了 SecretId+SecretKey 成对——成对请用 key_drop--pair。- ⚠️ 坑:
getpass.getpass()在无 tty 环境(管道、CI、部分 Windows 终端)不会抛异常, 而是挂等/dev/tty导致命令卡死。先判sys.stdin.isatty()再决定走 getpass, 否则退回stdin.readline()并明确警告用户"输入会显示"。
兜底:直接调 store_secret.py(heredoc)
⚠️ heredoc 里的密值会进终端回滚缓冲区与屏幕,仅在无法用 key_drop 时采用:
python store_secret.py tencent_cos --scope secret --notes "腾讯云COS" \
--exposed --exposed-where "WorkBuddy聊天" --exposed-at "2026-08-28" <<'EOF'
AKIDxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxKEY
EOF
# 两行 = 密钥对(SecretId / SecretKey);单行 = 单值
# 输出 STORED_OK 即成功(回环校验通过,不打印明文)
- 落盘:
~/.ucvault_local/<label>.enc(DPAPI 密文)+<label>.meta.json(作用域/泄漏标注)。 - 权限:
icacls /inheritance:r /grant:r <USER>:F+chmod 600,仅当前用户可读。
取回
python get_secret.py tencent_cos # 默认隐藏密值(安全展示)
python get_secret.py tencent_cos --show # 显示明文(格式化输出,带 value:/secret_id: 标签前缀)
python get_secret.py tencent_cos --raw # 只输出裸值(脚本/管道用,2026-09-07 新增)
⚠️ 前缀坑(2026-09-07 实测教训):--show 输出是带 value: / secret_id:
标签前缀的格式化文本,整段当令牌塞进 HTTP 头/URL,会得到大面积 401/40013
"令牌失效"假象——令牌其实是好的。脚本取值一律用 --raw;用 --show 必须
sed -n 's/^value: *//p' 截掉前缀。任何工具的人类可读输出都不能直接当凭据用。
安全要点(落地约束)
- 明文零落盘:密钥只在
CryptUnprotectData解密后的内存里短暂存在;文件里永远是 DPAPI 密文。 - 不回显:
get_secret.py默认隐藏密值,--show才显;store 的回环校验不打印明文。 - 密钥不进命令行:密值走 stdin(heredoc),绝不用命令行参数传,防
ps/历史泄漏。 - 权限锁当前用户:写入后立即去继承 + 仅授权当前用户完全控制。
- 泄漏标注:
--exposed会在 meta.json 写EXPOSED_PENDING_ROTATION+ 暴露位置/时间,取用前先看到警告。
配套文件
store_secret.py— 加密写入(读 stdin;两行=密钥对,单行=单值;可选泄漏标注)。get_secret.py— 解密取回(默认隐藏密值,--show才显明文)。tools/key_drop.py— 推荐投递入口:隐藏输入 / 成对 / 记事本三种模式。vault.py/vault.cmd— 薄封装(put/get/list/rm);put仅支持单值。
应用侧读取约定(三级兜底)
应用不要自己解析 .enc,统一按此优先级取,改轮换只需换源头、不动代码:
1. 环境变量 服务器部署 / CI
2. 本机保险库 ~/.ucvault_local(DPAPI)
3. 项目内文件 .llm_key / .cos.conf(仅兜底)
参考实现见 medxpert-reg-mcp/cred.py(llm_key() / cos_cred(),通过
get_secret.py --show 取字段,只取长度/布尔判断,绝不打印密值)。
要点:取密值的代码永远不要 print 密值,也不要把它写进日志或异常信息。
真实案例
本机保险库 ~/.ucvault_local/tencent_cos.enc(Windows 即 %USERPROFILE%\.ucvault_local\tencent_cos.enc)即此法保管的一把腾讯云 COS 密钥(root 级,已标注 EXPOSED_PENDING_ROTATION,待用户在 CAM 禁用/轮换)。
版权与许可
- © 2026 SynomosAI。本技能按 MIT 许可证开源(见 LICENSE.md);软件依 LICENSE 使用,零数据收集。
- 知识版权声明:本作品汇集的方法论、协议流程与结构化知识,其编排与原创表达归 SynomosAI 所有;未经书面许可,不得复制、转载、摘编、转售或用于训练任何模型 / 商业系统。
- 免责声明:本作品按「现状」(AS IS)提供,不提供任何明示或暗示担保;使用风险由使用者自行承担,因使用所致任何损失作者不承担责任。
微信扫一扫