商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > ThinkPHP 8.0 如何做接口签名校验?时间戳与随机串防重放【加密】

ThinkPHP 8.0 如何做接口签名校验?时间戳与随机串防重放【加密】

  发布于2026-07-14 阅读(0)

扫一扫,手机访问

签名校验必须在中间件中实现,不能放控制器里——因为中间件是TP8请求生命周期里唯一能统一拦截、不侵入业务逻辑、且确保所有接口被覆盖的环节。控制器校验很容易重复或遗漏,一旦漏掉某个接口,重放攻击的风险就来了。

ThinkPHP 8.0 如何做接口签名校验?时间戳与随机串防重放【加密】

签名校验必须在中间件里做,不能放控制器

TP8 的请求生命周期里,中间件是唯一一个能统一拦截所有接口、同时不侵入业务逻辑的位置。要是把校验逻辑塞进控制器,要么每个方法里都得重复写 checkSign(),要么某个新接口忘了加——重放攻击就专挑漏掉的地方下手。

推荐新建中间件 app/middleware/ApiSignatureCheck.php,然后在 app/middleware.php 中全局注册(或者按路由分组绑定):

return [
    'api' => [
        app\middleware\ApiSignatureCheck::class,
    ],
];

注意中间件的顺序很关键:它必须在 ValidatePostSize 之后、业务控制器之前执行,否则可能读不到完整的请求体。

签名算法要固定参数顺序和编码规则

TP8 默认用 input() 获取参数,但这个方法对 GET/POST/JSON 混合请求的行为不一致。必须统一用 $request->param()(合并后的结果),再手动提取 signtimestampnonce,最后构造待签名字符串。

几个常见的坑:

  • 参数 key 必须按字典序排序(用 ksort()),否则客户端和服务端算出来的签名永远对不上。
  • 空值、null0 怎么处理?建议统一转成空字符串 '',避免 PHP 自动类型转换干扰哈希结果。
  • URL 编码要严格:用 rawurlencode() 而不是 urlencode(),后者会把空格转成 +,而标准签名要求空格转成 %20

核心逻辑片段示例:

$params = $request->param();
unset($params['sign']);
ksort($params);
$toSign = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
$expectedSign = hash_hmac('sha256', $toSign, config('api.sign_key'));

时间戳和随机串(nonce)必须查重且限时

只校验 timestamp 是否在 ±5 分钟以内是不够的——攻击者可以截获一次合法请求,反复重放同一套 timestamp+nonce+sign。必须把 nonce 存起来做去重。

推荐做法:

  • timestamp 校验:当前时间与请求时间差的绝对值 ≤ 300 秒,超过直接拒绝。
  • nonce 校验:用 Redis 存 setex api:nonce:{$nonce} 300 1,如果设置失败(返回 0)说明已经存在,直接拒掉。
  • 别用数据库存 nonce:高并发下写入延迟和锁竞争会导致误判或性能瓶颈。

Redis key 命名建议带上前缀,过期时间与 timestamp 窗口对齐,避免残留。同时 nonce 长度至少 16 位随机字符串(用 bin2hex(random_bytes(16))),防止被暴力枚举。

错误响应要统一且不泄露细节

签名失败时,别返回 "sign error: invalid timestamp" 这种提示——攻击者会通过响应差异来判断你校验了哪一步。所有失败都用同一个错误码和消息,比如 401 Unauthorized + {"code":401,"msg":"Unauthorized"}

另外,TP8 的异常处理机制默认会输出调试信息,线上环境务必确认 app_debugfalse,并且在中间件里用 throw new HttpException(401, 'Unauthorized') 而不是 exit/json,确保走统一的异常渲染流程。

还有一个容易被忽略的点:如果接口支持 CORS,签名中间件必须在 Cors 中间件之后运行,否则预检请求(OPTIONS)会被拦住。但 OPTIONS 本身不应该做签名校验,所以要在中间件里加一句:if ($request->method() === 'OPTIONS') return $next($request);

本文转载于:https://www.php.cn/faq/2816897.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注