fix(security): 修复 P4 封禁记录数据错位 + P5 枚举爆破宽限(方案 A)
P4 #17 手动封禁 Create 非 upsert 导致数据错位: - BanStore 新增 BanIP(ip, reason, duration):事务内清理该 IP 全部 既有记录(兼容历史脏数据)后插入单条封禁记录,计数清零; DisconnectConnection 改用(原裸 Create 为全仓库唯一调用点) - BanEntry.IPAddress 升级 uniqueIndex;InitDB 在 AutoMigrate 前 dedupeBanEntries 清理历史重复行(MySQL 1093 兼容写法) - IncrementFail 原子化:SQL 侧 fail_count+1,miss 时 OnConflict DoNothing 插入兜底并发竞态,回读计数 P5 #18 方案 A(按失败性质区分宽限): - RecordAuthFailure 新增 knownUser 参数:用户名不存在(枚举型 爆破)跳过 3 次宽限、首次触发即封第 1 档;用户名存在(真实 用户输错)保留宽限防误封 - 新增 UserStore.LoginExists(邮箱/裸用户名);五个失败调用点 接线(Web 登录查存在性;SMTP/IMAP/POP3 用登录名;LDAP 侧 存在性不可判定,保守按已知用户处理) - 封禁原因注明「未知用户名,跳过宽限」便于后台审计 新增 8 个测试(-race 通过):BanIP 单行 upsert、唯一索引约束、 16 协程并发计数精确、dedupe 清理/表不存在静默、未知用户即时 封禁、已知用户宽限回归、LoginExists 矩阵。
This commit is contained in:
@@ -73,7 +73,10 @@ func (h *AuthHandler) DoLogin(c *gin.Context) {
|
||||
|
||||
user, err := h.stores.Users.Authenticate(email, password)
|
||||
if err != nil {
|
||||
banned, failCount := h.stores.RecordAuthFailure(ip, h.banCfg.MaxFailAttempts, h.banCfg.BanDurationMin, "登录失败次数过多")
|
||||
// 区分失败性质:用户名存在(真实用户输错,保留宽限)vs
|
||||
// 用户名不存在(枚举型爆破,跳过宽限首次触发即封)
|
||||
knownUser := h.stores.Users.LoginExists(email)
|
||||
banned, failCount := h.stores.RecordAuthFailure(ip, h.banCfg.MaxFailAttempts, h.banCfg.BanDurationMin, "登录失败次数过多", knownUser)
|
||||
if banned {
|
||||
entry, _ := h.stores.Bans.GetByIP(ip)
|
||||
c.HTML(http.StatusForbidden, "banned", gin.H{"entry": entry})
|
||||
@@ -146,7 +149,8 @@ func (h *AuthHandler) LDAPLogin(c *gin.Context) {
|
||||
if err != nil {
|
||||
log.Printf("LDAP 认证失败: %v", err)
|
||||
|
||||
banned, failCount := h.stores.RecordAuthFailure(ip, h.banCfg.MaxFailAttempts, h.banCfg.BanDurationMin, "LDAP 登录失败次数过多")
|
||||
// LDAP 侧用户存在性无法判定,保守按已知用户处理(保留宽限防误封)
|
||||
banned, failCount := h.stores.RecordAuthFailure(ip, h.banCfg.MaxFailAttempts, h.banCfg.BanDurationMin, "LDAP 登录失败次数过多", true)
|
||||
if banned {
|
||||
entry, _ := h.stores.Bans.GetByIP(ip)
|
||||
c.HTML(http.StatusForbidden, "banned", gin.H{"entry": entry})
|
||||
|
||||
Reference in New Issue
Block a user