我来详细解释 PHP 中处理签名头(通常指 HTTP 请求中的签名验证)的方法。

常见的签名验证场景
HMAC-SHA256 签名验证
<?php
function verifySignature($payload, $signature, $secret) {
// 重建签名
$expectedSignature = hash_hmac('sha256', $payload, $secret);
// 使用安全比较防止时序攻击
return hash_equals($expectedSignature, $signature);
}
// 获取请求头和内容
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$payload = file_get_contents('php://input');
$secret = 'your_secret_key';
if (verifySignature($payload, $signature, $secret)) {
echo "签名验证通过";
} else {
http_response_code(401);
echo "签名验证失败";
}
生成签名
<?php
function generateSignature($payload, $secret) {
return hash_hmac('sha256', $payload, $secret);
}
// 发送请求时生成签名
$data = json_encode(['user_id' => 123, 'action' => 'login']);
$secret = 'your_secret_key';
$signature = generateSignature($data, $secret);
// 设置请求头
$headers = [
'Content-Type: application/json',
'X-Signature: ' . $signature
];
完整的签名验证中间件
<?php
class SignatureMiddleware {
private $secret;
private $timestampTolerance = 300; // 5分钟
public function __construct($secret) {
$this->secret = $secret;
}
public function validate() {
// 获取签名头
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_TIMESTAMP'] ?? '';
$nonce = $_SERVER['HTTP_X_NONCE'] ?? '';
if (empty($signature) || empty($timestamp) || empty($nonce)) {
throw new \Exception('缺少签名参数');
}
// 检查时间戳是否过期
if (abs(time() - $timestamp) > $this->timestampTolerance) {
throw new \Exception('请求已过期');
}
// 获取请求体
$payload = file_get_contents('php://input');
// 重建签名字符串
$signString = $timestamp . $nonce . $payload;
// 计算期望签名
$expectedSignature = hash_hmac('sha256', $signString, $this->secret);
// 验证签名
if (!hash_equals($expectedSignature, $signature)) {
throw new \Exception('签名验证失败');
}
return true;
}
}
// 使用中间件
try {
$middleware = new SignatureMiddleware('your_secret_key');
$middleware->validate();
echo "验证通过";
} catch (\Exception $e) {
http_response_code(401);
echo $e->getMessage();
}
发送带签名的请求
<?php
class SignatureClient {
private $secret;
private $baseUrl;
public function __construct($baseUrl, $secret) {
$this->baseUrl = $baseUrl;
$this->secret = $secret;
}
public function sendRequest($endpoint, $data = []) {
$timestamp = time();
$nonce = bin2hex(random_bytes(16));
$payload = json_encode($data);
// 构建签名字符串
$signString = $timestamp . $nonce . $payload;
$signature = hash_hmac('sha256', $signString, $this->secret);
// 设置请求头
$headers = [
'Content-Type: application/json',
'X-Timestamp: ' . $timestamp,
'X-Nonce: ' . $nonce,
'X-Signature: ' . $signature
];
// 使用 cURL 发送请求
$ch = curl_init($this->baseUrl . $endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [
'status' => $httpCode,
'response' => json_decode($response, true)
];
}
}
// 使用示例
$client = new SignatureClient('https://api.example.com', 'your_secret_key');
$result = $client->sendRequest('/api/user', ['user_id' => 123]);
使用 openSSL 签名
<?php
// 生成私钥和公钥
$config = [
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
];
$res = openssl_pkey_new($config);
openssl_pkey_export($res, $privateKey);
$publicKey = openssl_pkey_get_details($res)['key'];
// 使用私钥签名
function signWithPrivateKey($data, $privateKey) {
openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256);
return base64_encode($signature);
}
// 使用公钥验证
function verifyWithPublicKey($data, $signature, $publicKey) {
$decodedSignature = base64_decode($signature);
return openssl_verify($data, $decodedSignature, $publicKey, OPENSSL_ALGO_SHA256);
}
// 使用示例
$data = 'request data';
$signature = signWithPrivateKey($data, $privateKey);
$isValid = verifyWithPublicKey($data, $signature, $publicKey);
安全建议
<?php
// 1. 使用安全比较函数
// hash_equals() 防止时序攻击
if (hash_equals($expected, $provided)) {
// 签名匹配
}
// 2. 添加时间戳防止重放攻击
$timestamp = $_SERVER['HTTP_X_TIMESTAMP'] ?? 0;
if (abs(time() - $timestamp) > 300) { // 5分钟内有效
die("请求已过期");
}
// 3. 使用随机数防止重复请求
$nonce = $_SERVER['HTTP_X_NONCE'] ?? '';
// 检查 nonce 是否已经使用过(需要存储已使用的 nonce)
// 4. 限制请求频率
// 可以使用 Redis 或数据库记录请求频率
// 5. 使用 HTTPS 传输
// 签名头也应该通过 HTTPS 传输
这些示例展示了 PHP 中处理签名头的常见方法,选择哪种方式取决于你的具体需求和安全要求。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播