thinkphp接口签名必须绑定时间戳、随机串和hmac三者校验:时间戳需秒级unix时间、参与签名并严格校验±3分钟有效期;nonce须16位以上随机字符串、与全部参数共同参与hmac计算,并通过redis原子写入防重放;签名原文须标准化构造,验签逻辑统一置于中间件中,使用hash_equals防止时序攻击。

ThinkPHP 接口签名必须把时间戳、随机串和 HMAC 三者绑定校验,缺一不可。只做参数拼接+密钥哈希,不带动态因子,等于没设防;只传 timestamp 和 nonce 却不参与签名计算,攻击者截包就能重放。
时间戳必须参与签名且严格校验有效期
客户端生成秒级 Unix 时间戳(不是毫秒),通过请求头(如 X-Timestamp)或参数传入。服务端不做简单存在判断,而是:
- 用
abs(time() - (int)$client_ts) 控制容差(推荐 ±3 分钟,比 ±5 分钟更稳妥) - 拒绝所有未带时间戳或超时的请求,不进入后续逻辑
- 时间戳字段必须显式加入签名原文,不能仅用于校验而不参与 hash 计算
随机串(nonce)要一次性且原子化存储
nonce 是 16 位以上随机字符串(建议用 bin2hex(random_bytes(16)) 生成),它不是“防重复”的装饰品,而是防重放的关键锚点:
- 必须和 timestamp、全部业务参数一起参与 HMAC-SHA256 签名,否则无法绑定上下文
- 服务端收到后立即执行 Redis 原子写入:
SET nonce:{$app_id}:{$nonce} 1 EX 180 NX(EX 180 对应时间窗口,NX 保证仅首次写入成功) - 不查旧值、不删旧键,写入即生效;失败则直接返回 401
HMAC 签名原文必须标准化构造
签名不是对任意字符串哈希,而是对严格定义的字符串做 hash_hmac('sha256', $data, $secret)。构造规则如下:
- 合并全部参数:GET 参数 + 解析后的原始 body(JSON 或表单),剔除
sign、app_id等非业务字段 - 对参数数组执行
ksort($params, SORT_STRING),确保字典序一致 - 每个 value 单独
rawurlencode()(不是urlencode()),再拼成k1=v1&k2=v2格式 - 追加
×tamp={$ts}&nonce={$nonce}&appid={$app_id},最后拼上密钥:$str = $sorted_params . $secret
验签逻辑必须放在中间件中统一拦截
不能在控制器里零散写,必须用 ThinkPHP 6/7 的中间件(如 app/middleware/CheckSign.php),并满足:
- 注册位置在
AllowCrossDomain之后、业务逻辑之前 - 用
$request->header('x-sign')取签名,用$request->raw()或$request->getInput()拿原始 body - 比对必须用
hash_equals(),防止时序攻击 - 失败时抛
HttpException(401),不返回任何业务数据或提示细节
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











