php如何防止小程序接口盗刷_php加限流防护策略【教程】
技术百科
絕刀狂花
发布时间:2026-01-27
浏览: 次 小程序请求不带 Referer,需用微信签名机制(sign/timestamp/nonce)校验+Redis 按 openid 限流+idempotency_key 幂等+ Nginx 兜底防护,且时间单位须统一。
为什么直接用 $_SERVER['HTTP_REFERER'] 拦不住小程序请求
小程序发起的 wx.request 请求默认不带 Referer,服务端读到的 $_SERVER['HTTP_REFERER'] 是空或不可信值。靠它做来源校验等于没设防。
真正有效的识别依据是微信签名机制——每次请求都应携带 sign、timestamp、nonce,且服务端需用约定密钥重算签名比对。
-
前端调用前必须用
md5(timestamp + nonce + secret)生成sign -
后端收到后先校验
timestamp是否在 5 分钟有效窗口内(防重放) - 再用相同逻辑重算
sign,严格区分大小写和拼接顺序 - 验证通过才进入后续逻辑,否则统一返回
401 Unauthorized
用 Redis 实现用户级 QPS 限流(非 IP 级)
小程序用户登录态是 openid,不是 IP。按 IP 限流会误伤同一 WiFi 下多个用户;按 openid 限流才合理。
推荐使用 Redis 的 INCR + EXPIRE 原子组合,避免竞态条件:
// 示例:每分钟最多 30 次接口调用
$openid = $this->getOpenidFromToken(); // 从 Authorization header 或 POST 中提取
$key = "rate_limit:{$openid}:api_v1_submit";
$redis->incr($key);
$redis->expire($key, 60);
if ($redis->get($key) > 30) {
throw new Exception('Request limit exceeded', 429);
}
- 务必用
$redis->expire()而非SET key val EX 60,因INCR可能创建 key 导致过期失效 - 若用 phpredis 扩展,确保版本 ≥ 5.3.0,老版本
expire()对不存在 key 返回 false 易漏判 - 不要在限流前查
GET再INCR,这会产生竞态,必须依赖原子操作
接口幂等性必须由客户端传 idempotency_key
防止恶意脚本反复提交(比如抽奖、下单),光靠频率限制不够,得靠业务层幂等控制。

要求小程序在请求头或参数中带上唯一 idempotency_key(如 UUID v4),服务端用它作为 Redis 键存处理状态:
- 收到请求先
SETNX idempotency_key:xxx processing 300(5 分钟过期) - 若设置成功,执行业务逻辑并最终写入结果到
idempotency_key:xxx:result - 若设置失败,直接返回缓存的结果(或查 DB 确认是否已成功)
- 注意:不能只依赖数据库唯一索引,因为网络超时后客户端可能重试,而 DB 插入可能已成功但响应未达
别忽略 Nginx 层的基础防护兜底
PHP 层限流失效时(比如 FPM 崩溃、代码绕过),Nginx 的 limit_req 是最后一道防线。
配置示例(按 $http_x_wx_openid 限流,需小程序主动透传):
map $http_x_wx_openid $openid_key {
default $http_x_wx_openid;
}
limit_req_zone $openid_key zone=api_per_user:10m rate=30r/m;
location /api/ {
limit_req zone=api_per_user burst=5 nodelay;
fastcgi_pass php-fpm;
}
- 必须用
map提取 header,不能直接用$http_x_wx_openid做 zone key,否则空值会打满一个桶 -
burst=5允许短时突发,但nodelay表示不延迟排队,超了直接 503,避免请求堆积拖垮 PHP - 这个配置无法替代 PHP 层签名和幂等校验,只是防流量冲击的辅助手段
最易被忽略的是时间窗口一致性:Redis 过期时间、签名 timestamp 容差、Nginx limit_req 的 rate 单位,三者必须统一用秒或分钟,混用会导致限流形同虚设。
# 的是
# 后端
# 多个
# 微信
# 最多
# 推荐使用
# 而非
# 客户端
# 再用
# redis
# 堆
# 接口
# 数据库
# 为什么
# 不带
# red
# node
# 前端
# map
# 需用
# php
# wifi
# nginx
# 小程序
# 服务端
# timestamp
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- 如何在Golang中处理数据库事务错误_回滚和日志
- 如何在Golang中使用replace替换模块_指
- Windows10怎么用“讲述人”读屏辅助 Win
- 如何使用正则表达式批量替换重复的星号-短横模式为固
- 如何在Golang中实现并发消息队列消费者_Gol
- Windows7怎么找回经典开始菜单_Window
- Win11怎么修复系统文件_使用sfc命令修复Wi
- C++如何编写函数模板?(泛型编程入门)
- Win11无法识别耳机怎么办_解决Win11插耳机
- 如何在Golang中实现微服务负载均衡_Golan
- C#如何在一个XML文件中查找并替换文本内容
- 如何处理“XML格式不正确”错误 常见XML we
- 如何在 Go 后端安全获取并验证前端存储的 JWT
- Win11如何设置环境变量 Win11添加和修改系
- Windows10怎么卸载预装软件_Windows
- 如何在 Python 中将 ISO 8601 时间
- Golang如何实现基本的用户注册_Golang用
- Python邮件系统自动化教程_批量发送解析与模板
- 为什么Go需要go mod文件_Go go mod
- php下载安装包怎么选_threadsafe与nt
- Python文件操作优化_大文件与流处理解析【教程
- Mac如何使用听写功能_Mac语音输入打字【效率技
- Win11怎么更改电脑名称_Windows 11修
- Django 密码修改后会话失效的解决方案
- PHP的FastAdmin架构适合二次开发吗_特点
- MAC如何安装Git版本控制工具_MAC开发环境配
- Win11怎么硬盘分区 Win11新建磁盘分区详细
- c++如何打印函数堆栈信息_c++ backtra
- Python解释执行模型_字节码流程说明【指导】
- 如何在Golang中使用log包输出不同级别日志_
- Win11怎么关闭键盘按键音_Win11禁用打字声
- php打包exe后无法写入文件_权限问题解决方法【
- c++如何实现多态性_c++ 虚函数表原理与动态绑
- Windows10怎么查看硬件信息_Windows
- windows如何修改文件默认打开方式_windo
- Linux如何安装Golang环境_Linux下G
- Windows10电脑怎么设置虚拟内存_Win10
- MAC如何启用访达侧边栏显示_MAC Finder
- Win10系统怎么查看端口状态_Windows10
- php485在php5.6下能用吗_php485旧
- Win11搜索不到蓝牙耳机怎么办 Win11蓝牙驱
- Python项目维护经验_长期演进说明【指导】
- php485在macos下怎么配置_php485
- 如何使用正则表达式提取以编号开头、后接多个注解的逻
- Win11怎么关闭搜索历史_Win11清除设备上的
- Win11声音太小怎么办_Windows 11开启
- Windows10电脑怎么设置防火墙出站规则_Wi
- 小程序里php怎么变mp4_小程序调用php生成m
- php文件怎么变mp4保存_php输出视频流保存为
- Mac如何设置动态壁纸?(让桌面动起来)

QQ客服