PHP 回调地址校验

wen PHP项目 2

本文目录导读:

PHP 回调地址校验

  1. 微信支付/微信小程序 回调校验(加密签名)
  2. 支付宝回调校验(RSA2 签名)
  3. 通用 HMAC-SHA256 签名校验(自定义开发)
  4. 简化版:使用框架验证(Laravel / Symfony 等)
  5. 校验的通用原则(重要提示)
  6. 建议

在 PHP 中,回调地址校验(通常用于微信支付、支付宝等第三方接口)是安全关键环节,主要目的是确认请求确实来自对方服务器,防止伪造回调。

以下是几种常见的校验方式和完整代码示例:


微信支付/微信小程序 回调校验(加密签名)

微信的回调通常包含 signaturetimestampnonce 三个参数,需要按以下步骤校验。

<?php
/**
 * 微信回调地址校验(GET 请求用于URL验证,POST 用于业务通知)
 * 适用于微信支付、公众号、小程序等
 */
// 你的 Token(在微信公众平台/商户平台设置)
$token = 'your_wechat_token_here';
// 获取请求参数(GET 用于首次配置验证,POST 用于后续业务回调)
$signature = $_GET['signature'] ?? '';
$timestamp = $_GET['timestamp'] ?? '';
$nonce     = $_GET['nonce'] ?? '';
$echostr   = $_GET['echostr'] ?? '';
// 1. 将 token、timestamp、nonce 三个参数进行字典序排序
$tmpArr = [$token, $timestamp, $nonce];
sort($tmpArr, SORT_STRING);
// 2. 将三个参数字符串拼接成一个字符串进行 sha1 加密
$tmpStr = implode($tmpArr);
$tmpStr = sha1($tmpStr);
// 3. 开发者获得加密后的字符串可与 signature 对比
if ($tmpStr === $signature) {
    // 签名验证通过
    // 如果是 URL 验证(GET 请求),直接返回 echostr
    if (!empty($echostr) && $_SERVER['REQUEST_METHOD'] === 'GET') {
        echo $echostr;
        exit;
    }
    // 如果是业务回调(POST),继续处理业务逻辑
    // $postData = file_get_contents('php://input');
    // $xml = simplexml_load_string($postData, 'SimpleXMLElement', LIBXML_NOCDATA);
    echo 'success';
    exit;
} else {
    // 验证失败
    http_response_code(403);
    echo 'Invalid signature';
    exit;
}

支付宝回调校验(RSA2 签名)

支付宝使用 RSA2 算法验证签名,需要用到支付宝公钥。

<?php
/**
 * 支付宝回调验签(RSA2)
 */
// 支付宝公钥(从支付宝开放平台获取,非应用私钥)
$alipayPublicKey = '-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
-----END PUBLIC KEY-----';
// 获取 POST 请求的参数(支付宝以 POST 表单形式提交)
$postData = $_POST;
// 移除 sign 和 sign_type 参数
$sign = $postData['sign'] ?? '';
unset($postData['sign'], $postData['sign_type']);
// 1. 将所有参数按 key 升序排序
ksort($postData);
// 2. 拼接成 URL 查询字符串
$content = urldecode(http_build_query($postData));
// 3. 使用支付宝公钥验证签名
$result = openssl_verify(
    $content,
    base64_decode($sign),
    $alipayPublicKey,
    OPENSSL_ALGO_SHA256  // RSA2 使用的算法
);
if ($result === 1) {
    // 验签成功
    // 检查 is_success / trade_status 等业务参数
    if ($postData['trade_status'] === 'TRADE_SUCCESS' || $postData['trade_status'] === 'TRADE_FINISHED') {
        // 处理业务逻辑...
        echo 'success';  // 必须返回 success,支付宝才认为处理成功
        exit;
    }
    echo 'success';
    exit;
}
// 验签失败
http_response_code(400);
echo 'fail';
exit;

通用 HMAC-SHA256 签名校验(自定义开发)

如果你是自己开发的系统(如开放平台 API),推荐使用 HMAC 签名。

<?php
/**
 * 通用 HMAC-SHA256 回调校验
 * 签名规则:HMAC-SHA256(secret, query_string 排序拼接)
 */
// 预设的共享密钥(与调用方约定)
$secret = 'your_shared_secret_key';
// 获取请求头或参数中的签名
$providedSign = $_SERVER['HTTP_X_SIGNATURE'] ?? ($_GET['sign'] ?? '');
// 获取所有 POST 参数(排除 sign 字段)
$params = $_POST;
if (isset($params['sign'])) {
    unset($params['sign']);
}
// 1. 将参数按 key 排序
ksort($params);
// 2. 拼接成字符串
$data = [];
foreach ($params as $key => $value) {
    $data[] = $key . '=' . $value;
}
$signStr = implode('&', $data);
// 3. 计算 HMAC-SHA256
$computedSign = hash_hmac('sha256', $signStr, $secret);
// 4. 安全比较(防止时序攻击)
if (hash_equals($computedSign, $providedSign)) {
    // 签名有效,继续处理业务
    echo 'ok';
    exit;
}
http_response_code(401);
echo 'Invalid signature';
exit;

简化版:使用框架验证(Laravel / Symfony 等)

如果你使用 Laravel,可以创建自定义中间件来统一处理:

// app/Http/Middleware/VerifyCallbackSignature.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class VerifyCallbackSignature
{
    public function handle(Request $request, Closure $next)
    {
        $secret = config('services.callback_secret');
        $signature = $request->header('X-Callback-Signature');
        // 按你自己的规则计算签名
        $payload = $request->getContent();
        $computed = hash_hmac('sha256', $payload, $secret);
        if (!hash_equals($computed, $signature)) {
            return response()->json(['error' => 'Invalid signature'], 403);
        }
        return $next($request);
    }
}

然后在路由中使用:

Route::post('payment/callback', [PaymentController::class, 'callback'])
    ->middleware('verify.signature');

校验的通用原则(重要提示)

原则 说明
使用 hash_equals() 不用 比较签名,使用 hash_equals() 防止时序攻击
验签后不要立刻信任数据 还要验证金额、订单号、商户号等业务数据是否匹配
幂等处理 回调可能重复发送,需使用订单状态判断重复请求
返回特定响应 如微信需返回 success,支付宝返回 success,否则会重试
限制 IP 可配置白名单 IP(微信/支付宝固定 IP 段)
HTTPS 必备 回调地址必须支持 HTTPS
日志记录 记录所有回调请求(保留原始数据)便于排查

建议

  1. 使用官方 SDK:微信和支付宝都有官方 PHP SDK,里面封装了验签逻辑,避免自己实现出错。
  2. 测试环境:上线前使用沙箱环境测试回调。
  3. 安全审计:定期检查回调日志,防止重放攻击。

如果需要针对某个具体平台(微信支付、支付宝、银联等)做详细配置,可以告诉我,我提供更详细的配置步骤。

抱歉,评论功能暂时关闭!