这个方案核心是隐藏文件真实地址,文件存放在网站根目录外(无法直接HTTP访问),或用权限锁定,只允许PHP。读取用动态临时下载链接替代,定时随机变更链接,随机密钥不可预测,过期自动失效。
校验来源域名、IP地址、链接有效期、签名,彻底防止盗链、批量下载、服务器过载。单IP限速、单文件并发下载限制、错误次数锁定,防止服务器过载。PHP代理输出文件,支持大文件断点续传
1.配置文件(config.php)统一管理密钥、有效期、限制规则,方便修改。
<?php
// 下载配置 - 核心参数
define('DOWNLOAD_KEY', 'your_secure_random_key_2013'); // 安全密钥(自行修改)
define('LINK_EXPIRE', 10); // 临时链接有效期(分钟)
define('ALLOW_HOST', 'yourdomain.com'); // 允许的域名(防盗链)
define('MAX_IP_DOWNLOAD', 3); // 单IP同时最大下载数
define('BLOCK_ERROR_TIMES', 10); // 错误10次锁定IP1小时
define('FILE_DIR', '/home/www/private_files/'); // 文件真实存放目录(根目录外!)
// 数据库连接(用于记录IP下载、错误次数)
$db = new mysqli('localhost', '数据库用户', '数据库密码', '数据库名');
if ($db->connect_errno) die('数据库连接失败');
$db->set_charset('utf8');
?>
2.生成临时下载链接(create_link.php)定时随机生成临时链接,前端/后台调用此方法获取可下载地址
<?php
require 'config.php';
// 生成临时下载链接
// $file_id 文件唯一ID / $real_filename 真实文件名
function create_download_link($file_id, $real_filename) {
$time = time();
$expire = $time + (LINK_EXPIRE * 60);
// 随机密钥 + 签名 = 链接不可伪造、定时失效
$rand = md5(uniqid(mt_rand(), true));
$sign = md5($file_id . $expire . $rand . DOWNLOAD_KEY);
// 构造临时下载地址(真实地址完全隐藏)
return "download.php?fid={$file_id}&expire={$expire}&rand={$rand}&sign={$sign}&name={$real_filename}";
}
// ---------------- 使用示例 ----------------
// 后台/前端列表调用,输出定时随机链接
// echo create_download_link(101, '软件安装包.zip');
?>
3.下载处理入口(download.php)核心验证+代理下载,所有校验都在这里执行,不通过直接拒绝
<?php
require 'config.php';
header('Content-Type: text/html; charset=utf-8');
// 1. 获取链接参数
$fid = intval($_GET['fid'] ?? 0);
$expire = intval($_GET['expire'] ?? 0);
$rand = trim($_GET['rand'] ?? '');
$sign = trim($_GET['sign'] ?? '');
$filename = trim($_GET['name'] ?? '');
$ip = $_SERVER['REMOTE_ADDR'];
// 安全过滤文件名
$filename = basename($filename);
// 2. 基础校验
if (!$fid || !$expire || !$rand || !$sign || !$filename) {
die('链接参数错误');
}
// 3. 链接过期校验
if ($expire < time()) {
die('下载链接已过期,请刷新页面重新获取');
}
// 4. 签名校验(防止伪造链接)
$true_sign = md5($fid . $expire . $rand . DOWNLOAD_KEY);
if ($sign !== $true_sign) {
die('链接无效,禁止盗链');
}
// 5. 防盗链:校验来源域名
$referer = $_SERVER['HTTP_REFERER'] ?? '';
if ($referer && !str_contains($referer, ALLOW_HOST)) {
die('非法来源,禁止盗链');
}
// 6. IP错误次数锁定(防暴力试链接)
$lock = $db->query("SELECT * FROM ip_block WHERE ip='$ip' AND block_time>".time())->fetch_assoc();
if ($lock) die('IP已临时锁定,请1小时后重试');
// 7. 单IP下载并发限制(防过载)
$downloading = $db->query("SELECT COUNT(*) AS num FROM download_log WHERE ip='$ip' AND status=0 AND create_time>".(time()-300))->fetch_assoc();
if ($downloading['num'] >= MAX_IP_DOWNLOAD) {
die('同时下载文件过多,请完成后再下载');
}
// 8. 验证真实文件是否存在(根目录外,无法直接访问)
$real_file = FILE_DIR . $fid . '.dat'; // 用文件ID存储,无规律
if (!file_exists($real_file) || !is_file($real_file)) {
// 记录错误次数
$db->query("INSERT INTO ip_error(ip,create_time) VALUES('$ip',".time().")");
$err = $db->query("SELECT COUNT(*) AS num FROM ip_error WHERE ip='$ip' AND create_time>".(time()-3600))->fetch_assoc();
if ($err['num'] >= BLOCK_ERROR_TIMES) {
$db->query("REPLACE INTO ip_block(ip,block_time) VALUES('$ip',".(time()+3600).")");
}
die('文件不存在');
}
// 9. 记录下载日志(防刷、统计)
$db->query("INSERT INTO download_log(file_id,ip,create_time,status) VALUES($fid,'$ip',".time().",0)");
$log_id = $db->insert_id;
// 10. 输出文件(PHP代理下载,隐藏真实地址)
$file_size = filesize($real_file);
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-Length: '.$file_size);
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no'); // 关闭Nginx缓冲,支持大文件
// 大文件分块输出(防内存溢出)
$chunk = 1024 * 1024;
$handle = fopen($real_file, 'rb');
while (!feof($handle)) {
echo fread($handle, $chunk);
ob_flush();
flush();
}
fclose($handle);
// 11. 标记下载完成
$db->query("UPDATE download_log SET status=1 WHERE id=$log_id");
exit;
?>
4.数据库建表SQL
-- 下载日志(限制并发、统计)
CREATE TABLE `download_log` (
`id` int NOT NULL AUTO_INCREMENT,
`file_id` int NOT NULL,
`ip` varchar(50) NOT NULL,
`create_time` int NOT NULL,
`status` tinyint NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `ip` (`ip`),
KEY `file_id` (`file_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- IP错误次数(防攻击)
CREATE TABLE `ip_error` (
`id` int NOT NULL AUTO_INCREMENT,
`ip` varchar(50) NOT NULL,
`create_time` int NOT NULL,
PRIMARY KEY (`id`),
KEY `ip` (`ip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- IP锁定表
CREATE TABLE `ip_block` (
`ip` varchar(50) NOT NULL,
`block_time` int NOT NULL,
PRIMARY KEY (`ip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 本站原创内容,转载请注明来源:https://www.liutonghui.com/36
评论列表(0条)
暂无评论