PHP 怎么PHP 签名头

wen PHP项目 2

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

PHP 怎么PHP 签名头

常见的签名验证场景

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 中处理签名头的常见方法,选择哪种方式取决于你的具体需求和安全要求。

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