diff --git a/internal/service/password_reset.go b/internal/service/password_reset.go index 406f7b7..775080b 100644 --- a/internal/service/password_reset.go +++ b/internal/service/password_reset.go @@ -113,6 +113,12 @@ func (s *PasswordResetService) ResetPassword(ctx context.Context, token, newPass return errors.New("重置Token数据异常") } + // 安全修复: 验证通过后立即删除Token,防止Replay攻击 + // Token消耗后立即失效,避免密码重置被重复触发 + if err := s.cache.Delete(ctx, cacheKey); err != nil { + return fmt.Errorf("清理重置Token失败: %w", err) + } + user, err := s.userRepo.GetByID(ctx, userID) if err != nil { return errors.New("用户不存在") @@ -122,9 +128,6 @@ func (s *PasswordResetService) ResetPassword(ctx context.Context, token, newPass return err } - if err := s.cache.Delete(ctx, cacheKey); err != nil { - return fmt.Errorf("清理重置Token失败: %w", err) - } return nil } @@ -229,6 +232,10 @@ func (s *PasswordResetService) ResetPasswordByPhone(ctx context.Context, req *Re return errors.New("验证码不正确") } + // 安全修复: 验证通过后立即删除验证码,防止Replay攻击 + // 在密码重置完成前消耗验证码,确保同一验证码只能使用一次 + s.cache.Delete(ctx, codeKey) + // 获取用户ID cacheKey := fmt.Sprintf("pwd_reset_sms:%s", req.Phone) val, ok := s.cache.Get(ctx, cacheKey) @@ -241,6 +248,9 @@ func (s *PasswordResetService) ResetPasswordByPhone(ctx context.Context, req *Re return errors.New("验证码数据异常") } + // 安全修复: 立即删除手机->用户ID映射,防止重复使用 + s.cache.Delete(ctx, cacheKey) + user, err := s.userRepo.GetByID(ctx, userID) if err != nil { return errors.New("用户不存在") @@ -250,10 +260,6 @@ func (s *PasswordResetService) ResetPasswordByPhone(ctx context.Context, req *Re return err } - // 清理验证码 - s.cache.Delete(ctx, codeKey) - s.cache.Delete(ctx, cacheKey) - return nil }