webman本身不拦截短信攻击,真正起作用的是你挂载tinywan/limit-traffic中间件(非webman/rate-limiter)、全局或路由组级配置、按手机号限流、穿透代理取真实ip、启用redis lua原子操作、返回429及标准限流头,并确保下游短信调用设超时。

Webman 本身不拦截短信攻击,它只提供可插拔的限流、黑名单、异常处理能力——真正起作用的是你挂什么中间件、挂在哪、参数怎么设、底层 Redis 是否可靠。
必须用 tinywan/limit-traffic 而不是 webman/rate-limiter
面对每秒几百次的短信发送请求(比如 /api/send-sms),webman/rate-limiter 的旧版本在高并发下存在 Redis 竞态,可能多放行 1–2 次,对暴力爆破就是缺口;而 tinywan/limit-traffic 用 Lua 脚本封装 INCR+EXPIRE,保证原子性。
- 安装:
composer require tinywan/limit-traffic - 配置示例(按手机号限流):
'rule' => ['mobile' => 'mobile', 'limit' => 5, 'period' => 86400],注意mobile必须从$request->get('mobile')显式提取,不能依赖前端传参校验 - 别用
@RateLimiter注解挂单个接口——泛洪攻击是全路径打,必须全局中间件或路由组级挂载
IP 黑名单必须穿透代理 + 过滤私有地址
直接读 $_SERVER['REMOTE_ADDR'] 在 Nginx 反代后全是 127.0.0.1 或内网 IP,黑名单形同虚设。
- Nginx 配置必须加:
set_real_ip_from 10.0.0.0/8; real_ip_header X-Real-IP;(根据你实际代理网段调整) - PHP 层取 IP 逻辑要过滤私有地址:
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE),否则内网地址会被误加入黑名单 - 黑名单存储建议用 APCu(毫秒级响应),Redis 仅用于跨机器同步;避免用文件或数据库查黑名单,扛不住瞬时峰值
异常响应必须统一为 429 + 标准头
tinywan/limit-traffic 默认返回 429,但若你用了自定义异常处理器(如 Tinywan\ExceptionHandler\Handler),可能把限流异常吞成 500 或静默丢弃。
- 确认
config/exception.php中未覆盖TooManyRequestsHttpException类型 - 检查中间件顺序:限流中间件必须在路由解析之后、控制器执行之前生效,否则
sendSms方法里抛异常就晚了 - 前端依赖
X-Rate-Limit-Limit、X-Rate-Limit-Remaining做退避重试,漏这些头等于没防护——tinywan/limit-traffic自动注入,webman/rate-limiter需手动写$response->withHeader()
别忽略真实瓶颈:短信服务商 API 调用本身
限流拦得住请求,拦不住下游短信接口超时或失败导致的重试风暴。很多积压问题根源不在 Webman,而在 app/queue/Handler.php 里没设超时。
- 所有
curl调用必须带CURLOPT_TIMEOUT_MS,建议 ≤3000ms;别用file_get_contents同步发请求 - Redis 队列投递短信任务时,别用
Client::send()(内存队列,进程重启即丢),改用Redis::send()持久化 - 日志里如果连续出现相同
handle()开始时间戳,说明某条短信请求卡死整个消费者进程,立刻查是否调用了无 timeout 的 MySQL 或 Redis 操作
最常被跳过的环节是:没验证 Nginx 是否真把 X-Real-IP 透下来,也没确认 Redis Lua 脚本是否在生产环境启用——这两处任一失效,限流和黑名单就直接归零。











