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

您的位置: 首页 > 文章列表 > 编程开发 > ThinkPHP 5.1 防止 API 接口参数篡改的签名算法【防刷】

ThinkPHP 5.1 防止 API 接口参数篡改的签名算法【防刷】

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

扫一扫,手机访问

先抛一个问题:API 参数被篡改,最常见的原因是什么?很多人第一反应是“没加签名”。但真实情况往往更扎心——签名算法本身就有漏洞。比如,漏掉了timestamp校验,nonce没去重,甚至在拼接字符串时用了urlencode而不是rawurlencode,结果服务端和客户端算出来的HMAC-SHA256值永远对不上。

开篇之前,先说一个咱们都懂的常识:签名验证这件事,一定要放在中间件里做。

签名验证必须放在中间件里

新建中间件很简单:执行 php think make:middleware CheckApiSign,然后在 app/middleware.php 中注册它。关键是要保证这个中间件排在路由解析之后、业务控制器之前。

在中间件的 handle 方法里,上来就提取签名。如果校验失败,直接返回 json(['code'=>401,'msg'=>'sign invalid']) 并终止流程。千万别放行到控制器里面。

【关键】控制器里写验签,等于把锁装在屋里——恶意参数已经触发了日志写入、库存扣减甚至数据库事务,验签就完全失去了意义。

统一获取原始参数并归一化

第一步:调用 $raw = $request->getInput() 一次性读取 php://input 流。如果为空,可以 fallback 到 file_get_contents('php://input')

第二步:根据 $request->header('content-type') 进行分支处理:
如果包含 application/json,就用 json_decode($raw, true)
否则,用 parse_str($raw, $body)

第三步:合并参数:$params = array_merge($request->get(), $body ?: []);。然后对每个值执行 trim(str_replace(["\r","\n"], '', (string)$v)),把隐藏字符清除干净。

第四步:剔除非业务字段。注意顺序:先 isset() 判断是否存在,避免 Notice 报错,再 unset($params['sign'], $params['signature'], $params['timestamp'], $params['nonce']);

构造签名原文的三要素

方法一:基础拼接(适用于 GET 或简单表单)。
$params 执行 ksort($params, SORT_STRING)
然后遍历拼接 rawurlencode($k).'='.rawurlencode($v),用 & 连接;
再追加 ×tamp=xxx&nonce=yyy(注意:timestamp 必须是客户端传入的 Unix 秒级时间戳);
最后拼上密钥:$data = $query_string . '×tamp=' . $ts . '&nonce=' . $nonce . '&key=' . config('api.sign_key');

方法二:带上 HTTP 方法和路径(可以防止接口被挪用)。
$method = strtoupper($request->method());$uri = parse_url($request->url(), PHP_URL_PATH);
签名原文格式为:$method . "\n" . $uri . "\n" . $query_string . "\n"
【关键】换行符必须是 \n,不能是 \r\n,否则哈希值会错位。

计算与比对签名

计算签名:用 hash_hmac('sha256', $data_string, config('api.sign_key'), true) 计算二进制签名,再 base64_encode() 编码为字符串。

从 Header 中取签名:$clientSign = $request->header('X-Signature');。千万不要用 $request->param('sign'),后者可能会被业务参数污染。

比对时,用 hash_equals($serverSign, $clientSign) 来防止时序攻击。如果 ThinkPHP 5.1 没有这个函数,在低风险场景下可以用 ==

校验 timestamp:abs(time() - $ts) > 300,超时直接拒绝。nonce 存 Redis:Redis::setex('nonce:' . $nonce, 600, 1),如果已存在,则验签失败。

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

热门关注