fix: 优化邮件推送,防止邮件轰炸攻击

This commit is contained in:
dujiao-next committed 2026-05-14 09:45:30 +08:00
1 parent 0ad0eb7927
commit faacb3f5e3
2 files changed
+39 -1

No files matched your search

+7 -1
View File
@@ -59,7 +59,13 @@ func (c *Client) EnqueueOrderStatusEmail(payload OrderStatusEmailPayload, opts .
if err != nil {
return err
}
options := append([]asynq.Option{asynq.Queue(c.defaultQueue)}, opts...)
// 限制重试次数与保留期,避免 SMTP 长时间不可用时邮件任务堆积成雪崩;
// 调用方可通过 opts 覆盖。
options := append([]asynq.Option{
asynq.Queue(c.defaultQueue),
asynq.MaxRetry(3),
asynq.Retention(24 * time.Hour),
}, opts...)
_, err = c.client.Enqueue(task, options...)
return err
}
+32
View File
@@ -74,6 +74,23 @@ func (c *Consumer) handleOrderStatusEmail(_ context.Context, task *asynq.Task) e
logger.Debugw("worker_order_status_email_skip_invalid_payload", "order_id", payload.OrderID)
return nil
}
// 消费侧二次校验开关:避免 SMTP/订单通知关闭后,旧任务(含 retry/scheduled 中的任务)继续触发发送。
if c.SettingService != nil && c.Config != nil {
smtpSetting, smtpErr := c.SettingService.GetSMTPSetting(c.Config.Email)
if smtpErr != nil {
// fail-closed:读不到设置就视作关闭,不重试,避免 DB 抖动放大成邮件雪崩。
logger.Warnw("worker_order_status_email_load_smtp_setting_failed", "order_id", payload.OrderID, "error", smtpErr)
return nil
}
if !smtpSetting.Enabled || !smtpSetting.OrderNotificationEnabled {
logger.Debugw("worker_order_status_email_skip_setting_disabled",
"order_id", payload.OrderID,
"smtp_enabled", smtpSetting.Enabled,
"order_notification_enabled", smtpSetting.OrderNotificationEnabled,
)
return nil
}
}
order, err := c.OrderRepo.GetByID(payload.OrderID)
if err != nil {
logger.Warnw("worker_order_status_email_fetch_order_failed", "order_id", payload.OrderID, "error", err)
@@ -83,6 +100,21 @@ func (c *Consumer) handleOrderStatusEmail(_ context.Context, task *asynq.Task) e
logger.Debugw("worker_order_status_email_skip_order_not_found", "order_id", payload.OrderID)
return nil
}
// 游客订单的"取消"通知价值极低(用户大概率已离开),但极易被滥用为退信轰炸载体:
// 攻击者可用伪造邮箱批量下单等过期取消触发发送。统一不发,保留登录用户与其它状态。
if order.UserID == 0 {
emailStatus := strings.TrimSpace(payload.Status)
if emailStatus == "" {
emailStatus = order.Status
}
if emailStatus == constants.OrderStatusCanceled {
logger.Debugw("worker_order_status_email_skip_guest_canceled",
"order_id", order.ID,
"order_no", order.OrderNo,
)
return nil
}
}
var receiverEmail string
var locale string
if order.UserID != 0 {