默认 gin.Default() 不适用于短信网关,因其不校验 Content-Type、无读写超时、不限制请求体大小、暴露敏感日志;须改用 gin.New() 并配置 Recovery、日志过滤、BodySizeLimit、指数退避重试、goroutine 池及 SDK 超时对齐。

为什么默认 gin.Default() 不能直接用在短信网关上
它开了太多危险默认项:不校验 Content-Type、不设读写超时、不限制请求体大小、不屏蔽敏感日志,连 gin.H 都可能把错误堆栈打到响应里。真实压测中,攻击者发个 10MB 的伪造 Content-Type: application/json 请求,就能触发 OOM 或 panic。
必须显式替换为 gin.New(),并手动注册必要中间件:
-
gin.Recovery()要保留,但得改写 handler —— 默认会把 panic 详情吐给客户端,得改成统一返回{"code":500,"msg":"internal error"} - 加
gin.LoggerWithWriter()并过滤掉phones、params字段,防止手机号明文落盘 - 用
gin.BodySizeLimit(2 (2MB)硬限制请求体,短信参数不可能超过几 KB - 所有路由必须走
router.Use()加validateContentType中间件,只放行application/json和multipart/form-data
如何安全解析和校验 /api/v1/send-message 请求
别信前端传来的任何字段 —— 尤其是 businessNo 和 phones。常见漏洞是业务编号绕过、手机号注入 Redis 命令、模板参数 XSS。
实操要点:
立即学习“go语言免费学习笔记(深入)”;
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
-
businessNo必须查白名单表(如 MySQL 或本地 map),不能只做正则匹配;非法值直接 403,不进后续逻辑 -
phones字段要拆成切片后逐个调libphonenumber.Parse()校验格式 + 国家码,中国号必须带 +86 前缀 -
params里的每个字符串,用html.EscapeString()处理后再填入模板,防短信内容注入(比如params=["<script>alert(1)</script>"]) - 所有 JSON 解析必须用
json.Unmarshal()配合结构体 tag,禁用map[string]interface{}—— 否则攻击者可构造深层嵌套耗尽栈空间
Redis 存验证码时怎么防爆破和遍历
直接用 SET sms:verify:138****1234 123456 EX 300 看似简单,但会被扫描器暴力遍历 key。生产环境必须加哈希和混淆。
正确做法:
- key 拼接用
sms:verify:+sha256(phone + salt),salt 是服务启动时生成的随机字符串,不硬编码 - 写入前先
EXISTS检查是否已存在,避免被刷频;若 60 秒内同一号码已发过,直接返回 429 - 验证码值不存纯数字,而是
base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%d", phone, code))),增加破解成本 - Redis 连接必须启用
tls.Config,禁用redis-cli直连,密码通过环境变量注入而非配置文件
异步发短信时 worker panic 为何会导致整个网关挂掉
因为 goroutine 内部 panic 不会自动传播到主线程,但未 recover 的 panic 会让 worker 退出,channel 缓冲区满后新请求卡在 select 上,最终 HTTP handler 超时、连接堆积、系统假死。
关键修复点:
- 每个 worker 启动时必须包一层
defer func(){if r := recover(); r != nil { log.Printf("worker panic: %v", r) }}() - channel 缓冲区大小设为
1000,但要配监控:当len(chan) > 800时告警,说明下游短信接口异常或 worker 数不够 - 失败重试必须带指数退避:
time.Sleep(time.Second * time.Duration(math.Pow(2, float64(attempt)))),最多 2 次 - 别用
go sendSMS(req),要用workerPool.Submit(req)统一调度,池子大小固定为 CPU 核数 × 2,防 goroutine 泄漏
最易被忽略的是:短信 SDK 的 timeout 参数必须设得比 HTTP handler 的 WriteTimeout 小至少 1 秒,否则超时后还在发,结果发成功了但客户端早已断开。

















