安全: 修复页面密码零值绕过,导出不再泄露投票者 IP
页面密码(P0,2026-11-05 复核发现) - 旧实现 `ver, _ := Session.Get(key).(uint); if ver != user.PagePasswordVer` 把「session 里没有授权记录」(断言失败得 0)与「新增列默认值 0」判成相等, 升级瞬间所有存量页面密码静默失效:匿名访客不带 cookie 即可读箱主页。 - 收口为 pagePasswordAuthorized:缺 key 一律视为未授权, key 名由 pagePassKey 统一生成,Pager / ListAPI / AskStatusAPI 三处共用。 - 新增迁移 v6 回填 page_password_ver / ask_password_ver(仅仍为 0 且已设密码的行, 幂等),让模型语义与「改过一次密码」一致,并作废升级前的布尔授权。 - AskStatusAPI 的 L4 查询补日志(保持与 Pager 一致的 fail-open 口径)。 导出(P1) - JSON 导出的投票提交不再输出 voter_ip。库里照常保留, 防刷、取证与站方 CLI 处置不受影响,只是不进箱主的自助导出文件。 测试 - route/question/page_password_test.go:8 个用例钉住零值/类型不符/轮换失效等边界。 - internal/db/page_password_migration_test.go:回填只碰该碰的行且幂等。 验证:WSL2 Debian + PostgreSQL 全量测试通过;隔离实例实测 ver=0 匿名访问返回密码页、API 401,输对密码后放行、改密后旧会话立即失效, 删除迁移账本后重启可正确回填。
This commit is contained in:
@@ -885,6 +885,29 @@ func (db *users) GetHotTags(ctx context.Context, limit int) ([]string, error) {
|
||||
return hot, nil
|
||||
}
|
||||
|
||||
// backfillPagePasswordVer 给「已设置密码但版本号仍为 0」的存量账号补一次版本号。
|
||||
//
|
||||
// 背景:PagePasswordVer / AskPasswordVer 是 2026-11-05 新增的列,AutoMigrate 只补默认值 0。
|
||||
// 而 0 同时是「session 里没有授权记录」时类型断言的零值,两者相等会让页面密码判定恒为通过
|
||||
// (见 route/question/page.go 的 pagePasswordAuthorized 注释)。
|
||||
// 把存量行统一置 1:语义与「改过一次密码」一致,升级前发出的旧授权(布尔 true)
|
||||
// 也会因类型不符而自然作废,用户重输一次密码即可。
|
||||
//
|
||||
// 幂等:只更新仍为 0 的行,重复执行不会把版本号越推越大。
|
||||
func backfillPagePasswordVer(db *gorm.DB) error {
|
||||
if err := db.Model(&User{}).
|
||||
Where("page_password <> '' AND page_password_ver = 0").
|
||||
Update("page_password_ver", 1).Error; err != nil {
|
||||
return errors.Wrap(err, "backfill page password version")
|
||||
}
|
||||
if err := db.Model(&User{}).
|
||||
Where("ask_password <> '' AND ask_password_ver = 0").
|
||||
Update("ask_password_ver", 1).Error; err != nil {
|
||||
return errors.Wrap(err, "backfill ask password version")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *users) UpdatePagePassword(ctx context.Context, id uint, password string) error {
|
||||
user, err := db.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user