如何在PHP服务端与客户端实现HLS流的Token安全机制?
HLS流Token签名验证实现方案(PHP服务端 + hls.js客户端)
核心原理
不管是wmsAuthSign还是signature,本质都是基于约定的密钥+请求参数生成哈希签名:客户端只能使用服务端签发的带签名URL请求流资源,服务端收到请求后重新计算签名并对比,验证合法性后才返回流文件。
一、服务端(PHP)实现签名生成
1. 定义签名规则
选择用于生成签名的核心参数(建议包含):
exp:URL过期时间戳(Unix时间,防止URL永久有效)stream:唯一流ID(区分不同流资源)ip:客户端IP(可选,绑定请求IP增强安全性)- 密钥
SECRET_KEY:服务端保密密钥,绝对不能暴露给客户端
2. 生成带签名的播放URL代码示例
<?php // 服务端专属保密密钥,自行替换为高强度字符串 define('SECRET_KEY', 'your_strong_secret_key_here'); /** * 生成带签名的HLS流URL * @param string $streamId 流唯一标识 * @param int $expireSeconds URL有效期(秒,例:3600=1小时) * @param string|null $clientIp 绑定的客户端IP(可选) * @return string */ function generateSignedHlsUrl(string $streamId, int $expireSeconds = 3600, ?string $clientIp = null): string { // 计算过期时间戳 $expire = time() + $expireSeconds; // 整理签名参数,按参数名排序避免顺序影响签名结果 $params = [ 'stream' => $streamId, 'exp' => $expire ]; if ($clientIp) { $params['ip'] = $clientIp; } ksort($params); $rawString = http_build_query($params); // 生成HMAC-SHA256签名 $signature = hash_hmac('sha256', $rawString, SECRET_KEY); // 拼接最终URL(替换为你的流基础地址) $baseUrl = "http://your-domain.com/hls/{$streamId}.m3u8"; $signedUrl = $baseUrl . '?' . $rawString . '&signature=' . $signature; // 若需适配wmsAuthSign格式,可改为: // $wmsAuthSign = "wmsAuthSign=stream={$streamId}&exp={$expire}&signature={$signature}"; // $signedUrl = $baseUrl . '?' . $wmsAuthSign; return $signedUrl; } // 示例:生成绑定当前客户端IP、有效期1小时的流URL $signedUrl = generateSignedHlsUrl('live_stream_001', 3600, $_SERVER['REMOTE_ADDR']); echo $signedUrl; ?>
二、服务端(PHP)实现签名校验
在处理HLS流文件(m3u8、ts)请求的PHP脚本中,添加校验逻辑:
<?php define('SECRET_KEY', 'your_strong_secret_key_here'); /** * 校验HLS请求的签名合法性 * @return bool */ function validateHlsSignature(): bool { // 提取URL参数 $stream = $_GET['stream'] ?? ''; $exp = $_GET['exp'] ?? 0; $signature = $_GET['signature'] ?? ''; $clientIp = $_GET['ip'] ?? $_SERVER['REMOTE_ADDR']; // 1. 检查URL是否过期 if (time() > (int)$exp) { return false; } // 2. 按生成时的规则重新拼接原始字符串 $params = [ 'stream' => $stream, 'exp' => $exp, 'ip' => $clientIp ]; ksort($params); $rawString = http_build_query($params); // 3. 重新计算签名并对比(用hash_equals防止时序攻击) $calculatedSignature = hash_hmac('sha256', $rawString, SECRET_KEY); return hash_equals($calculatedSignature, $signature); } // 校验不通过则返回403 if (!validateHlsSignature()) { http_response_code(403); exit('Forbidden: Invalid signature or expired URL'); } // 校验通过,返回流文件(可改用nginx X-Accel-Redirect优化性能) $streamPath = "/path/to/your/hls/files/{$_GET['stream']}.m3u8"; if (file_exists($streamPath)) { header('Content-Type: application/vnd.apple.mpegurl'); readfile($streamPath); } else { http_response_code(404); exit('Stream not found'); } ?>
三、客户端(hls.js)使用带签名的URL
客户端只需获取服务端生成的带签名URL,直接传入hls.js即可:
// 从服务端接口获取带签名的流URL const signedHlsUrl = 'http://your-domain.com/hls/live_stream_001.m3u8?stream=live_stream_001&exp=1718xxxxxx&ip=192.168.1.100&signature=abcdef123456...'; const video = document.getElementById('hls-video'); const hls = new Hls(); hls.loadSource(signedHlsUrl); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, () => { video.play(); });
关键注意事项
- 密钥保密:
SECRET_KEY必须仅在服务端存储,绝对不能出现在客户端代码中。 - 参数一致性:生成和校验签名时的参数顺序、编码规则必须完全一致,否则校验会失败。
- 强制过期:必须添加
exp参数,避免URL被永久滥用。 - 性能优化:高并发场景下,建议将校验逻辑迁移到Nginx层面(如Lua脚本),减少PHP性能开销。
内容的提问来源于stack exchange,提问作者amirhosein karimi
相关产品推荐
相关产品推荐

