v7.9.0:找回密码上线——一枚邮箱验证码背后的 5 个安全设计
狸花云控制台今天上线了「找回密码」。听起来是个小功能:填邮箱、收验证码、设新密码,三步完事。但真正做起来,难点不在流程,而在怎么不让这个功能变成攻击者的工具。这篇记录一下 v7.9.0 的实现,以及同天发布的侧栏角色分层和阶梯定价。
一、找回密码:流程只有三步,防线有五层
前端入口很克制——登录框下方一行「忘记密码?」,点开是弹窗,邮箱、验证码、新密码依次出现。后端两个接口:
POST /api/forgot/send-code # 发验证码
POST /api/forgot/reset # 校验 + 改密码
五个安全设计,每一条都对应一类真实攻击:
| 防线 | 参数 | 防什么 |
|---|---|---|
| 防邮箱枚举 | 未注册邮箱也返回「如果该邮箱已注册,验证码已发送」 | 攻击者批量探测哪些邮箱注册过 |
| 验证码时效 | 10 分钟过期 | 拦截到旧邮件也无法利用 |
| 尝试次数 | 错误 5 次作废 | 暴力枚举 6 位数字(理论百万分之一/次) |
| 发送冷却 | 60 秒内不可重发 | 邮件轰炸、刷接口 |
| 一次一用 | 重置成功后立即删除记录 | 验证码复用 |
其中防枚举是最容易被忽略的。直觉写法是查不到邮箱就返回 404「该邮箱未注册」——方便、友好,但等于给攻击者开了一个免费的用户名册查询接口。现在的做法是统一话术,只是不真发信,同时在审计日志里记一笔 forgot-send-skip,管理员侧依然能看到异常探测行为。
if (!u || u.status !== 'active') {
auditLog(req, 'auth', 'forgot-send-skip', { detail: email, ok: false })
return ok(res, { ok: true, message: '如果该邮箱已注册,验证码已发送' })
}
新密码的规则也收紧了:至少 8 位,必须同时包含字母和数字。所有成功重置都会写审计日志(forgot-reset),谁在什么时间重置了密码,可追溯。
二、侧栏角色分层:三种人看到三个控制台
之前所有登录用户看到的侧栏是一样的,只是部分页面进去后显示「无权限」。这在逻辑上没错,体验上很糟——用户能看到「节点管理」却点不进去,只会觉得系统坏了。
v7.6.0 把侧栏按角色拆成三层:
- 普通用户:我的空间(服务器 / 工具箱 / 订阅链接 / 域名)+ 购买与服务
- 运维(operator):额外看到运营中心(看板 / 代码管理 / VLESS 管理)
- 管理员:再额外看到基础设施(节点 / 模板)和用户与计费
不只是前端隐藏。后端同步收紧:/api/pve/guests 对普通用户剥离节点信息,/api/pve/status 直接返回空。前端隐藏是体验,后端过滤才是安全——这条原则这次彻底落实了。
三、自定义套餐:阶梯定价上线
商城新增「自定义配置」,CPU / 内存 / 磁盘 / 时长自由组合。定价采用阶梯而非线性——线性定价(1 核 5 元、8 核 40 元)会鼓励用户买一堆小机器而不是一台大机器,对宿主机调度是灾难。阶梯价让大单更划算:
CPU: 1核¥5 2核¥9 4核¥16 8核¥28
内存: 1G¥2 2G¥4 4G¥7 8G¥12 16G¥22
时长: 月×1 季×2.7(9折) 年×9.6(8折)
技术上有两个点值得一提:
1. 临时套餐不落库。自定义配置生成 planOverride 直接注入下单流程,不污染 plans 列表,管理页不会被一堆一次性配置淹没。
2. 下单前做容量预检。按目标节点剩余磁盘(留 5% 缓冲)判断能否开通,不足直接返回「无库存」,前端同时禁用购买按钮——不让用户付了钱才发现开不出来。
四、顺便做的:清理 30 条僵尸设备码
代码管理里躺着 64 条设备码,盘点后发现只有 14 条关联着有效订单,20 条是 VLESS 待分配库存,剩下 30 条是测试残留和已退款/已取消订单的遗留。全部备份后清理,代码总数从 64 降到 34。
这类「数据卫生」平时不起眼,但设备码列表是运营看板的数据源——垃圾数据不清,所有统计都是假的。
小结
v7.9.0 一天的量:一个安全向的新功能(找回密码)、一次权限模型的重构(角色分层)、一个商业化能力(阶梯定价)、一轮数据清理。控制台版本号、更新日志与本文同步公开。
狸花网络 · 让每一台设备都有身份 🐾