本文目录导读:

在 PHP 中实现“服务端签名”(Server Signing),通常指的是对 API 请求或敏感数据进行数字签名,以验证数据的完整性和来源的真实性(防止篡改或伪造)。
常见的场景包括:
- 微信/支付宝支付回调验证(需按平台提供的签名算法验签)。
- 对外暴露的 API 接口(双方约定公私钥或密钥,生成签名)。
- 文件/消息完整性校验(防止传输过程中内容被修改)。
下面我会给出一个通用的、基于 HMAC-SHA256(对称密钥)和 RSA-SHA256(非对称公私钥)的 PHP 签名和验签实现方案,你可以根据实际需求选择使用。
基于 HMAC(对称密钥)的签名
这种方式使用相同的密钥进行签名和验签,速度快,适合内部服务间调用。
签名生成步骤(PHP)
<?php
function generateSignature(array $params, string $secretKey): string {
// 1. 按照参数名 ASCII 码升序排序
ksort($params);
// 2. 拼接成字符串 key1=value1&key2=value2...
$queryString = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
// 3. 使用 HMAC-SHA256 计算签名
$signature = hash_hmac('sha256', $queryString, $secretKey);
return $signature;
}
// 示例使用
$params = [
'name' => 'Alice',
'amount' => 100,
'timestamp' => time()
];
$secret = 'my-secret-key-123';
echo generateSignature($params, $secret);
// 输出类似:c5f3a7b8...
?>
验签步骤(PHP)
<?php
function verifySignature(array $params, string $signature, string $secretKey): bool {
// 1. 注意:如果传过来的参数中包含签名字段,需先排除
$signatureFromRequest = $params['signature'] ?? '';
unset($params['signature']);
// 2. 重新计算签名
$computedSignature = generateSignature($params, $secretKey);
// 3. 比较(防止时序攻击,使用 hash_equals)
return hash_equals($computedSignature, $signatureFromRequest);
}
?>
基于 RSA(非对称公私钥)的签名
适用于需要对外暴露 API,或同时需要签名和加密的场景,客户端用私钥签名,服务端用公钥验签。
准备工作
- 生成 RSA 密钥对(Linux/Mac):
openssl genrsa -out private_key.pem 2048 openssl rsa -in private_key.pem -pubout -out public_key.pem
签名生成(PHP 服务端用私钥签名)
<?php
function rsaSign(array $params, string $privateKeyFilePath): string {
// 1. 将参数排序并拼接成待签字符串
ksort($params);
$content = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
// 2. 读取私钥并签名
$privateKey = file_get_contents($privateKeyFilePath);
$pkeyId = openssl_get_privatekey($privateKey);
openssl_sign($content, $signature, $pkeyId, OPENSSL_ALGO_SHA256);
openssl_free_key($pkeyId);
// 3. 返回 Base64 编码的签名
return base64_encode($signature);
}
// 示例使用
$params = ['order_id' => '12345', 'amount' => 199.9];
$signature = rsaSign($params, '/path/to/private_key.pem');
echo $signature;
?>
验签(PHP 服务端用公钥验签)
<?php
function rsaVerify(array $params, string $signature, string $publicKeyFilePath): bool {
// 1. 排除签名字段,重新拼接
unset($params['signature']);
ksort($params);
$content = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
// 2. 读取公钥
$publicKey = file_get_contents($publicKeyFilePath);
$pubKeyId = openssl_get_publickey($publicKey);
// 3. 验签(注意 base64_decode 还原签名)
$result = openssl_verify($content, base64_decode($signature), $pubKeyId, OPENSSL_ALGO_SHA256);
openssl_free_key($pubKeyId);
return $result; // true 或 false
}
?>
常见问题与最佳实践
| 问题 | 解决方案 |
|---|---|
| 参数中包含中文/特殊字符 | 使用 http_build_query 并指定 PHP_QUERY_RFC3986 保证编码一致 |
| 签名验证总失败 | 检查排序是否一致(包括排除签名字段) 检查参数编码(urlencode)方式 使用 hash_equals 而非 防止时序攻击 |
| 私钥安全存储 | 不要写在代码里;使用环境变量或外部密钥管理服务(AWS KMS / HashiCorp Vault) |
| 时间戳/随机数防重放 | 在签名参数中加入 timestamp 和 nonce,服务端验签时检查时间差和是否使用过 |
完整示例:一个带签名的 API 请求流程
服务端(签名发送方)
// 1. 准备参数
$params = [
'type' => 'transfer',
'to' => 'user_123',
'amount' => 50.00,
'ts' => time()
];
// 2. 生成签名
$params['sign'] = generateSignature($params, $secret);
// 3. 发送请求(curl / Postman)
接收方(验签)
// 1. 获取 POST 数据
$receivedParams = $_POST;
// 2. 验签
$valid = verifySignature($receivedParams, $receivedParams['sign'], $secret);
if (!$valid) {
http_response_code(403);
echo '签名验证失败';
exit;
}
// 3. 继续业务逻辑
echo '通过';
- 内部服务:推荐 HMAC-SHA256(简单快速)。
- 对外 API 或需要非对称能力:推荐 RSA-SHA256(公钥验签,私钥签名)。
- 注意:务必按照双方约定的排序规则、编码方式和算法保持一致。
如果你需要具体某个平台(如支付宝、微信)的签名代码,请告诉我,我可以给出针对性的实现。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播