验签必须放在中间件里,不能在控制器中实现;因为中间件是thinkphp请求生命周期中唯一能在路由匹配、参数解析和控制器实例化前介入的环节,若在控制器验签,非法请求已触发日志、数据库查询或敏感操作,失去第一道防线意义。

验签必须放在中间件里,不能在控制器里写
ThinkPHP 的请求生命周期中,中间件是唯一能在路由匹配、参数解析、控制器实例化之前介入的环节。一旦进到控制器,$_POST 和 $_GET 已被合并,$request->param() 已自动解码,甚至模型查询都可能触发——攻击者靠一次非法请求就能刷出错误日志、耗尽数据库连接、或执行敏感操作。
常见错误包括:把验签逻辑写在控制器方法开头、或塞进验证器规则里(比如 'sign' => 'require|check_sign'),结果非法请求已打日志、查了库、发了短信才被拦下。
- 必须在
app/middleware.php中注册中间件,且确保它排在ValidateRequest之后、但远在所有业务中间件和控制器之前 - 若注册位置靠后(比如放在跨域中间件或日志中间件之后),非法请求的
Origin头可能被绕过,或错误日志已落盘 - 中间件里要用
$request->getRawBody()一次性读取原始请求体,避免后续业务拿不到 JSON 数据
签名原文必须包含 timestamp、nonce 和全部业务参数
只对固定字段签名,或漏掉 timestamp / nonce,等于把防重放的钥匙交给攻击者。签名不是可选项,而是唯一可信锚点。
客户端和服务端拼出的字符串必须完全一致,否则 hash_hmac('sha256', $data_string, $secret_key) 必然不匹配。90% 的 Signature not match 错误源于参数没对齐。
- 统一用
$request->getInput()读原始体,再按Content-Type分流解析:application/json用json_decode($input, true),其余用parse_str($input, $params) - 显式合并 URL 参数:
array_merge($request->get(), $params),防止 GET+POST 混合时丢失 - 剔除
sign、signature、timestamp、nonce等字段后,对剩余参数ksort($params)升序排列 - 每个键值对单独
rawurlencode()编码,拼成k1=v1&k2=v2&...格式,末尾不加&,最后追加rawurlencode($secretKey)
Redis 存 nonce 必须用原子操作 SET + EX,不能分两步
高并发下,redis_set("nonce:{$nonce}", 1); redis_expire("nonce:{$nonce}", 300); 会漏判:两个请求几乎同时进来,都成功写入 key,但只有第一个能设 TTL,第二个覆盖了过期时间,导致去重失效。
真正安全的做法是利用 Redis 的原子性指令,确保“写入成功 = 首次使用 + 自动过期”一步到位。
- 用
$redis->set($nonce, '1', ['nx', 'ex' => 300]),其中nx表示仅当 key 不存在时设置,ex绑定 300 秒生命周期 - key 命名建议带
app_id隔离:nonce:{app_id}:{nonce},避免多租户冲突 - 绝对不要用数据库或 Session 存 nonce——前者延迟高、易锁表,后者跨机器不共享
- nonce 必须参与签名原文计算,否则攻击者可替换它而不影响签名
timestamp 校验要允许 ±180 秒偏差,且拒绝未来时间
写成 time() - $params['timestamp'] > 300 是典型错误:没处理 NTP 偏移和手机系统时间误差。当前服务器时间(2026年6月25日)若未同步 NTP,偏差可能超 2 秒;客户端用手机生成 timestamp,误差常达 5–10 秒。
硬卡 60 秒窗口既不合理也不安全,太短影响弱网体验,太长扩大重放窗口。
- 统一用
abs(time() - (int)$params['timestamp']) > 180,允许 ±3 分钟 -
timestamp必须从请求头(如X-Timestamp)读取,禁止从 URL 或 body 解析——否则 CDN 缓存后所有请求共享同一时间戳 - 必须额外校验
$params['timestamp'] ,拒绝未来时间(比如 2026年6月30日的请求) - 时间戳单位用秒级即可,毫秒反而增加时钟漂移风险
真正起作用的从来不是单个字段,而是签名、时间戳、nonce 三者的强绑定与严格时序控制。最容易被忽略的是:签名没覆盖 timestamp 和 nonce,或者 Redis 写 nonce 用了非原子操作——这两点一错,整个防线就形同虚设。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











