PHP如何限制视频下载权限_PHP限制视频下载权限措施【管控】
技术百科
絕刀狂花
发布时间:2026-01-19
浏览: 次 PHP生成带时效签名的视频URL需经stream.php中转校验exp过期时间与sig签名,分块输出视频流并支持Range请求,同时Web服务器须禁用视频目录直连。
用 PHP 生成带时效签名的视频 URL
直接暴露 video.mp4 这类静态路径,等于放弃所有权限控制。真正可行的方式是让视频资源不通过文件系统直连,而是经由 PHP 脚本中转,并在 URL 中嵌入一次性、有时效的签名。
核心逻辑:用户请求时,PHP 生成类似 /stream.php?file=lesson123.mp4&exp=1717025400&sig=abc123... 的链接,stream.php 收到后验证 exp 是否过期、sig 是否由服务端密钥正确签出,仅当全部通过才读取并输出视频流。
-
exp建议设为 Unix 时间戳,精确到秒,有效期控制在 5–30 分钟内足够 - 签名算法推荐
hash_hmac('sha256', $file . '|' . $exp, $secret_key),避免拼接漏洞 - 务必在
stream.php开头调用header('Content-Type: video/mp4')和header('Accept-Ranges: bytes'),否则浏览器无法拖拽播放 - 大视频必须用
fopen()+fread()分块输出,禁用readfile(),否则内存溢出
header('Content-Type: video/mp4');
header('Accept-Ranges: bytes');
header('Cache-Control: no-store, no-cache');
$file = $_GET['file'] ?? '';
$exp = (int)($_GET['exp'] ?? 0);
$sig = $_GET['sig'] ?? '';
if ($exp < time() || !hash_equals(hash_hmac('sha256', $file . '|' . $exp, 'your_secret_key'), $sig)) {
http_res
ponse_code(403);
exit;
}
$path = '/var/www/videos/' . basename($file);
if (!is_file($path)) {
http_response_code(404);
exit;
}
$fp = fopen($path, 'rb');
if (!$fp) {
http_response_code(500);
exit;
}
while (!feof($fp) && connection_status() == CONNECTION_NORMAL) {
echo fread($fp, 8192);
flush();
}
fclose($fp);
禁止 Nginx/Apache 直接访问视频目录
即使 PHP 层做了签名校验,如果 Web 服务器配置允许绕过 PHP 直接访问 /videos/ 下的文件,所有权限控制就形同虚设。
常见错误是把视频放在 public/ 或 htdocs/ 下却没加限制规则。
- Nginx 中,在对应 server 块里添加:
location ^~ /videos/ { deny all; } - Apache 中,在
.htaccess或虚拟主机配置里写:Require all denied - 更安全的做法是把视频目录放到 Web 根目录之外,例如
/var/www/private/videos/,彻底隔绝 HTTP 直连可能
防止 Referer 绕过和盗链(辅助手段)
单纯依赖 Referer 检查非常脆弱,但作为辅助策略可增加爬虫和简单盗链的成本。
在 stream.php 中加入 Referer 白名单判断,只允许来自你自己的域名或特定 CDN 域名的请求:
- 获取来源:
$referer = $_SERVER['HTTP_REFERER'] ?? '' - 白名单匹配建议用
parse_url($referer, PHP_URL_HOST)提取 host 后严格比对,不要用strpos或模糊匹配 - 注意:移动端 WebView、某些隐私浏览器、HTTPS→HTTP 跳转都会清空 Referer,不能作为唯一验证依据
注意 Range 请求与断点续传支持
现代浏览器播放视频时默认发起 Range: bytes=0- 等请求,若 PHP 脚本不处理 HTTP Range 头,会导致无法拖动进度条、加载卡顿甚至报错 ERR_CONNECTION_ABORTED。
关键点不是“要不要支持”,而是“不支持就会坏掉”。必须解析 $_SERVER['HTTP_RANGE'],计算起始偏移、长度,并返回正确的 206 Partial Content 状态码和响应头。
- 忽略 Range 请求直接全量输出,会导致 Chrome/Firefox 拒绝播放
- 使用
fseek($fp, $start)定位文件指针,再循环fread()输出指定字节数 - 必须设置
Content-Range、Content-Length、Accept-Ranges三个响应头
最易被忽略的是 Range 支持——很多开发者以为签名 URL 加上就万事大吉,结果用户反馈“视频播到一半就卡住”,排查半天才发现是 PHP 脚本没处理分片请求。这个细节不补全,整个方案在真实场景中就是不可用的。
# 的是
# 就会
# 放在
# 自己的
# 爬虫
# 并在
# 浏览器
# 设为
# mac
# public
# https
# http
# 循环
# 字节
# 指针
# stream
# chrome
# require
# access
# private
# var
# 算法
# 万事大吉
# php
# 状态码
# apache
# nginx
# 半天
# cdn
# location
# fopen
# strpos
# unix
# Length
# firefox
# webview
# Directory
# 形同虚设
# 盗链
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Win10怎样设置多显示器_Win10多显示器扩展
- 如何在Golang中配置代码格式化工具_使用gof
- Python爬虫项目实战教程_Scrapy抓取与存
- Windows驱动无法加载错误解决方法_驱动签名验
- PHP怎么接收前端传的时间戳_处理时间戳参数转换技
- php怎么下载安装并配置环境变量_命令行调用PHP
- 如何使用 Selenium 正确获取篮球参考网站球
- 如何使用Golang构建基础消息队列模拟_Gola
- 静态属性修改会影响所有实例吗_php作用域操作符下
- c++中的CRTP是什么 c++奇异递归模板模式【
- 如何在 Python 中将 ISO 8601 时间
- Mac如何查看电池健康百分比_Mac系统信息电源检
- Windows10蓝屏SYSTEM_SERVICE
- Windows怎样关闭Edge新标签页广告_Win
- Windows 10怎么隐藏特定更新补丁_Wind
- C++如何解析JSON数据?(nlohmann/j
- Python正则表达式实战_模式匹配说明【教程】
- c++中如何使用std::variant_c++1
- PythonDocker高级项目部署教程_多容器管
- Win11文件扩展名怎么显示_Win11查看文件后
- Win11怎么激活Windows10_Win11激
- Windows10系统怎么查看显卡驱动_Win10
- php文件怎么变mp4保存_php输出视频流保存为
- Go语言中CookieJar的持久化机制解析:内存
- Windows10系统怎么查看设备管理器_Win1
- 如何使用Golang指针与接口结合_实现方法调用和
- Windows 10自带杀毒软件在哪_Window
- php转exe用什么工具打包快_高效打包软件推荐【
- c++如何打印函数堆栈信息_c++ backtra
- Python字符串处理进阶_切片方法解析【指导】
- Python性能剖析高级教程_cProfileLi
- Win10如何卸载预装Edge扩展_Win10卸载
- C#如何使用XPathNavigator高效查询X
- Win11怎么卸载Photos应用_Win11卸载
- Windows系统文件被保护机制阻止怎么办_权限不
- Win11怎么查看局域网电脑_Windows 11
- 如何使用Golang encoding/json解
- Python 中将 ISO 8601 时间戳转换为
- Win10怎么更改用户名 Win10修改账户名称操
- php转mp4怎么保留字幕_php处理带字幕视频转
- Win11系统占用空间大怎么办 Win11深度瘦身
- c++如何使用std::bitset进行位图算法_
- Win11怎么更改电脑密码_Windows 11修
- 如何使用正则表达式批量替换重复的 *- 模式为固定
- Python数据抓取合法性_合规说明【指导】
- c++ nullptr与NULL区别_c++11空
- Win11如何隐藏桌面图标 Win11一键隐藏/显
- Win11怎么查看硬盘型号_Windows 11检
- Win11如何关闭小娜Cortana Win11禁
- Win11怎么清理C盘系统错误报告_Win11清理


QQ客服