fix(sec): 安全评审加固——goroutine panic 恢复 + 下单 body 上限 + 错误脱敏

- 后台 goroutine (notifyBiz/SyncPending) 加 defer recover,防 panic 拖垮进程
- 下单请求体 64KB 上限 (MaxBytesReader),防超大 body 内存 DoS
- 下单错误不再回传原始 err.Error(),内部详情只进日志
- (另: pay nginx 加 per-IP 限流 limit_req,服务器侧配置)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UQmqWmV67sXGLrb3U1XXn
This commit is contained in:
2026-07-03 23:53:11 +08:00
parent ef0fa39ce4
commit e0c7baa7cb
2 changed files with 26 additions and 5 deletions
+14
View File
@@ -253,6 +253,13 @@ func (s *OrderService) GetByOutTradeNo(outTradeNo string) (*model.Order, error)
// SyncPending 兜底:把近期待支付订单拿去主动查单,命中已支付则补记(防回调丢失)。
func (s *OrderService) SyncPending(ctx context.Context, maxAge time.Duration) {
// 后台循环调用;恢复 panic 防止拖垮整个进程。
defer func() {
if r := recover(); r != nil {
log.Printf("[query_sync] 发生 panic 已恢复: %v", r)
}
}()
var orders []model.Order
cutoff := time.Now().Add(-maxAge)
if err := s.db.Where("status = ? AND created_at > ?", model.OrderPending, cutoff).
@@ -295,6 +302,13 @@ func (s *OrderService) StartQuerySync(interval, maxAge time.Duration) {
// notifyBizByOutTradeNo 向业务系统推送「支付成功」webhook(签名)。成功则置 BizNotified,失败留待重试。
func (s *OrderService) notifyBizByOutTradeNo(outTradeNo string) {
// 该函数在 goroutine 中运行(applyPaid 异步触发 + 重试循环);Go 中未恢复的 goroutine panic 会整进程崩溃。
defer func() {
if r := recover(); r != nil {
log.Printf("[biz_notify] 订单 %s 回调发生 panic 已恢复: %v", outTradeNo, r)
}
}()
var o model.Order
if err := s.db.First(&o, "out_trade_no = ?", outTradeNo).Error; err != nil {
return