PHP 怎么PHP HMAC

wen PHP项目 3

本文目录导读:

PHP 怎么PHP HMAC

  1. 基本语法
  2. 常用示例
  3. 安全注意事项

在PHP中,HMAC(Hash-based Message Authentication Code)主要通过 hash_hmac() 函数实现,以下是详细的使用指南:

基本语法

string hash_hmac ( string $algo , string $data , string $key [, bool $binary = false ] )
参数 说明
$algo 哈希算法(如 'md5', 'sha1', 'sha256', 'sha512' 等)
$data 要计算的消息内容
$key 密钥
$binary 是否返回原始二进制输出(默认 false 返回十六进制字符串)

常用示例

基础 HMAC-SHA256 签名

$message = "这是要签名的消息";
$secretKey = "my-secret-key-12345";
// 生成签名(十六进制输出)
$signature = hash_hmac('sha256', $message, $secretKey);
echo "签名: " . $signature . "\n";
// 生成签名(原始二进制输出)
$binarySignature = hash_hmac('sha256', $message, $secretKey, true);
echo "二进制签名 (base64): " . base64_encode($binarySignature);

API 签名验证示例

// 服务器端生成签名
function generateSignature($data, $secretKey) {
    return hash_hmac('sha256', json_encode($data), $secretKey);
}
// 客户端验证签名
function verifySignature($data, $signature, $secretKey) {
    $expected = generateSignature($data, $secretKey);
    return hash_equals($expected, $signature); // 使用 hash_equals 防止时序攻击
}
// 使用示例
$payload = ['user_id' => 123, 'timestamp' => time()];
$key = 'api-secret-key-2024';
$signature = generateSignature($payload, $key);
echo "API 签名: " . $signature . "\n";
// 验证
$isValid = verifySignature($payload, $signature, $key);
echo "签名验证: " . ($isValid ? "通过" : "失败") . "\n";

带时间戳的 HMAC(防止重放攻击)

class HMACAuth {
    private $secretKey;
    private $timeout = 300; // 5分钟超时
    public function __construct($secretKey) {
        $this->secretKey = $secretKey;
    }
    public function generateToken($data) {
        $timestamp = time();
        $payload = json_encode([
            'data' => $data,
            'timestamp' => $timestamp
        ]);
        $signature = hash_hmac('sha256', $payload, $this->secretKey);
        return base64_encode(json_encode([
            'payload' => $payload,
            'signature' => $signature
        ]));
    }
    public function validateToken($token) {
        $decoded = json_decode(base64_decode($token), true);
        if (!$decoded || !isset($decoded['payload']) || !isset($decoded['signature'])) {
            return false;
        }
        // 验证签名
        $expectedSignature = hash_hmac('sha256', $decoded['payload'], $this->secretKey);
        if (!hash_equals($expectedSignature, $decoded['signature'])) {
            return false;
        }
        // 验证时间戳
        $payload = json_decode($decoded['payload'], true);
        $elapsed = time() - $payload['timestamp'];
        if ($elapsed > $this->timeout) {
            return false; // Token 过期
        }
        return $payload['data'];
    }
}
// 使用示例
$auth = new HMACAuth('super-secret-key');
$token = $auth->generateToken(['user' => 'john_doe']);
echo "生成的 Token: " . $token . "\n";
$result = $auth->validateToken($token);
echo "验证结果: " . ($result ? "有效 - 用户: " . $result['user'] : "无效") . "\n";

HMAC 与不同的哈希算法

$data = "Hello, World!";
$key = "my-key";
$algorithms = ['md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd160'];
echo "各种 HMAC 算法对比:\n";
foreach ($algorithms as $algo) {
    $signature = hash_hmac($algo, $data, $key);
    echo str_pad($algo, 12) . ": " . $signature . "\n";
}

HMAC 签名

function signFile($filePath, $secretKey) {
    $content = file_get_contents($filePath);
    return hash_hmac('sha256', $content, $secretKey);
}
// 使用
$fileSignature = signFile('document.pdf', 'file-signing-key');
echo "文件签名: " . $fileSignature;

安全注意事项

时序攻击防护

永远使用 hash_equals() 进行比较,而不是 或 :

// 安全
if (hash_equals($expected, $provided)) {
    // 验证通过
}
// 不安全 - 易受时序攻击
if ($expected === $provided) {
    // 不推荐
}

密钥管理

// 推荐:使用环境变量或配置文件
$secretKey = getenv('API_SECRET_KEY');
// 或从安全配置读取
$config = require 'config.php';
$secretKey = $config['hmac_key'];

完整的 HMAC 工具类

class HMACTool {
    /**
     * 生成 HMAC 签名
     */
    public static function sign($data, $key, $algo = 'sha256') {
        return hash_hmac($algo, $data, $key);
    }
    /**
     * 验证 HMAC 签名(安全比较)
     */
    public static function verify($data, $signature, $key, $algo = 'sha256') {
        $expected = self::sign($data, $key, $algo);
        return hash_equals($expected, $signature);
    }
    /**
     * 生成带签名的 URL
     */
    public static function signUrl($url, $key, $expiry = 3600) {
        $expiryTime = time() + $expiry;
        $signature = self::sign($url . $expiryTime, $key);
        return $url . (strpos($url, '?') === false ? '?' : '&') 
             . "expires={$expiryTime}&signature={$signature}";
    }
    /**
     * 验证签名 URL
     */
    public static function verifySignedUrl($url, $key) {
        $parsedUrl = parse_url($url);
        parse_str($parsedUrl['query'] ?? '', $params);
        if (!isset($params['signature']) || !isset($params['expires'])) {
            return false;
        }
        // 检查是否过期
        if (time() > $params['expires']) {
            return false;
        }
        // 重新构建 URL(去除签名参数)
        $baseUrl = strtok($url, '?');
        $queryParts = [];
        foreach ($params as $k => $v) {
            if ($k !== 'signature') {
                $queryParts[] = "$k=$v";
            }
        }
        $cleanUrl = $baseUrl . '?' . implode('&', $queryParts);
        // 验证签名
        return self::verify($cleanUrl . $params['expires'], $params['signature'], $key);
    }
}
// 使用示例
$key = 'url-signing-key';
$url = 'https://api.example.com/download/file123';
$signedUrl = HMACTool::signUrl($url, $key, 3600);
echo "签名URL: " . $signedUrl . "\n";
// 验证
$isValid = HMACTool::verifySignedUrl($signedUrl, $key);
echo "URL有效: " . ($isValid ? "是" : "否");
  1. 算法选择:优先使用 sha256sha512,避免使用 MD5 或 SHA1
  2. 密钥强度:使用足够长且随机的密钥(至少 32 字节)
  3. 比较方式:始终使用 hash_equals() 进行比较
  4. 密钥存储:不要在代码中硬编码密钥
  5. 时效性:为签名添加时间戳以防止重放攻击

HMAC 在 API 认证、JWT 实现、Webhook 验证等场景中非常常用,掌握这些用法对 PHP 开发很有帮助。

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