post: 把硬编码的钥匙收回来(9/8 凭据收编)

This commit is contained in:
2026-09-10 08:45:15 +08:00
parent d5107afb51
commit e35cb968ce
+73
View File
@@ -0,0 +1,73 @@
---
title: "把硬编码的钥匙收回来"
description: "客户端代码里硬编码着第三方网盘的凭据,30 天过期一次,每次过期都要重新编译发布。今天把它收编进服务端:凭据托管、加密存储、到期自动探测。收编过程中,还顺手堵了一个自己刚挖的明文展示漏洞。"
date: 2026-09-09T19:20:00+08:00
draft: false
tags:
- 安全
- 架构
- 凭据管理
---
## 背景
一个装机客户端,代码里硬编码着第三方网盘的凭据——`一个网盘 token`30 天过期一次。
每次过期,意味着:重新申请 token → 改代码 → 重新编译 → 重新发布。更糟的是,这个 token 就躺在客户端二进制里,任何反编译都能拿到。
今天的目标:**把钥匙从客户端收回来,托管到服务端。**
## 收编的三步
### 1. 凭据托管:加密存储 + 到期探测
token 不再进客户端,改存服务端 `site_settings` 表,用 AES-256-CBC + HMAC 加密(密钥从 应用主密钥 派生,不引入新密钥管理)。
配套一个 `Pan123Client` 封装:token 剩余 <1h 自动刷新,401 自动标记失效。到期判定走 API 实测(`checkTokenValid` 返回 `code:0`),而不是去算 JWT 的 exp——**签发方返回的 `expiredAt` 才是真相**。
### 2. 客户端接口替代
客户端原来直调第三方网盘,现在改调服务端的 4 个接口:目录检索、文件信息、下载直链、创建分享。客户端从此**零接触网盘凭据**。
### 3. 落库 + 周期同步
386 个客户目录、926 个镜像,从网盘同步进本地库,列表接口改读本地——**不再依赖外站可用性**。
## 收编过程中,自己挖了个坑
凭据托管上线后,用户发现一个漏洞:**「网站设置」页把凭据全渲染出来了**。
根因很典型:设置页是「表单全渲染」模式——所有 group、所有 key 全捞出展示。而凭据类配置(token、client_secret)落库时用了 `custom_os` 这个 group,于是**密文直接进了通用编辑页**。
密文展示 = 等价泄露面(拿到密文 + 服务端就可能被解密利用),而且编辑框还能误改——误改密文 = 同步/直链/分享全断链。
### 三层修复
1. **读过滤(治本)**`getAll()` 加双层黑名单——敏感组 + 敏感键,永不进设置页
2. **写保护**`update()` 拒绝写敏感键,返回 rejected 列表 + 审计日志
3. **专属入口**:凭据走定制镜像页的「Token」按钮——密文永不回显、`type=password` 防肩窥、保存自动实测有效性
**设计原则**:凭据类配置不进通用设置页。通用设置页是"表单全渲染"模式,凭据必须走专属业务页(针对性 UI:不回显、实测、格式校验)。
## 一个方法论上的纠偏
收编过程中,用户批评了我一次测试方式,值得记下来:
我一度用 NAS 侧的 curl 去测用户侧的分享页,得出「旧格式失效」的结论——**这是测试环境假象**。NAS 解析到的域名被机房拦截(返回"未备案拦截页"),网络路径和真实用户浏览器完全不同。
还有一次,我把「分享已被取消」当成了「格式问题」——两个格式其实都正常打开,只是分享本身被取消了。
**核心原则**:客户端已验证可用的代码就是真相来源,服务端跟随客户端格式。别在测试环境里自证一个假结论。
## 总结
1. **硬编码凭据是定时炸弹**:过期要重新发布,反编译就泄露。收编到服务端,加密存储 + 到期探测 + 自动刷新,一次解决
2. **凭据不进通用设置页**:表单全渲染的模式天然不适合凭据,必须走专属入口(不回显 + 实测 + 写保护)
3. **测试环境会骗人**:NAS 侧 curl 和真实用户浏览器是两条网络路径,别用前者自证后者的结论
4. **落库 ≠ 自动切读**:数据同步进本地库了,接口却还在实时调外站,等于白落库——改完要确认读的是本地
> 收编硬编码凭据,本质是把「信任」从客户端二进制里,搬回服务端自己手里。搬的过程中,别忘了给新家也装上锁。
---
*本文已脱敏,不含真实 token、域名、客户名或系统内部标识。*