PHP隐藏文件真实下载地址防盗链功能

  这个方案核心是隐藏文件真实地址,文件存放在网站根目录外(无法直接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

一个迷你版Smarty模板引擎,认识模板引擎原理(附代码)
上一篇 2013-11-19
基于jQuery+JSON的省市区三级无刷新联动
下一篇 2013-12-09

评论列表(0条)

  • 暂无评论

发表评论

captcha

相关推荐

  • PHP图片防盗链技术

      图片防盗链的核心原理:验证图片请求的域名来源,只允许本站加载图片,拒绝外部未授权网站引用图片地址,以达到防盗链的目的。   浏览器请求图片时,会执行HTTP_REFERER请求头,记录从哪个页面发起请求,用程序判断读取该来源的地址,是否是合法域名,是则输出图片,否则返回禁止图片或403。   方案1:基础防盗链   img.php &lt;?php // 允许访问的域名 $allow_hosts = [ 'localhost......

    2013-10-06
    34540
  • PHP动态滚屏无刷新加载数据

      滚屏加载,也称为滚动加载或懒加载,是一种网页设计技术,当用户滚动页面时,新内容会自动加载,无需点击&ldquo;下一页&rdquo;按钮。这种技术提高了用户体验,尤其是在处理大量数据时,如社交媒体、新闻网站和电子商务平台。   滚屏加载技术,就是使用Javascript监视滚动条的位置,每次当滚动条到达浏览器窗口底部时,触发一个Ajax请求后台PHP程序,返回相应的数据,并将返回的数据追加到页面底部,从而实现了动态加载,其实就是一个......

    2013-10-24
    22240
  • PHP框架的实现原理

      PHP框架的实现原理是通过采用MVC模式、路由机制、依赖注入、模板引擎和数据库操作等技术,构建一个结构清晰、易于维护和扩展的应用程序框架。下面是一个简单的PHP框架示例,演示了基本的实现原理:   简单的PHP框架文件结构 /simple_framework |-- app | |-- controllers | | |-- HomeController.php | |-- models | | ......

    2017-10-04
    15492
  • PHP实现相关文章推荐功能

      实现相关文章推荐功能通常需要使用更高级的推荐系统算法,对于查询到的相关文章,需要计算它们与当前文章的相似度。相似度可以根据文章的内容、标题、标签等属性进行计算。可以使用文本相似度算法(如余弦相似度、Jaccard相似度等)或基于内容的推荐算法来计算相似度。   假设有一个包含文章信息的数据库表 articles: CREATE TABLE articles ( id INT PRIMARY KEY, title VA......

    2021-05-24
    17410
  • ThinkPHP数据软删除及数据恢复功能

      ThinkPHP的软删除不是真正删除数据库数据,而是通过标记字段(如delete_time)记录删除状态,查询时自动过滤delete_time字段已标记删除的数据,支持数据恢复和真实删除,相当于为项目加了一个回收站功能,以避免数据被误删。   软删除依赖模型操作,不支持原生查询,需要数据表有删除标记字段(默认delete_time,类型:datetime/timestamp,允许null),需要引入ThinkPHP内置的SoftDe......

    2022-01-27
    10700
  • ThinkPHP文件上传类FileSystem自定义生成年月日目录

      FileSystem 是一个非常好用的文件上传扩展类,结合 Thinkphp 使用可以轻松的完成文件上传功能的开发。但是默认情况下 FileSystem 是按照&ldquo;年月日&rdquo;来生成上传日期目录的,长期使用下来就会有大量的&ldquo;Ymd&rdquo;目录,不方便管理,像我平时更新内容不多,如果以&ldquo;Ym&rdquo;的格式生成目录,按同一年同一月上传的文件放在一个日期目录中就方便管理多了。   需要......

    2023-04-23
    10342
  • 解决 PHP 8.1 隐式类型转换导致缩略图生成错误

      这几天解决了一个困扰已久的问题,我的博客系统在文章上传图片生成缩略图时,在本地测试环境没有任何问题,但在服务器环境中总是生成缩略图失败,报错:"Implicit conversion from float"。排查了 PHP 和环境扩展组件库、目录权限、图片格式大小等都没有问题。在百思不得其解时,我注意到了一个平时不太注意的细节,我在本地测试环境用的是 PHP 8.0,服务器上用的是 PHP 8.1,经过反复测试问题果然出在这里。  ......

    2023-09-04
    13310
  • ThinkPHP多入口文件多应用设置方法

      在使用ThinkPHP做项目开发时,为了让架构更清晰科学,提高安全和性能,方便维护,需要将多应用分配到各自对应的不同的入口文件上,而不是整个项目用一个入口文件。   此方法适用于ThinkPHP6/8,这是ThinkPHP默认的单应用结构: ├─app 应用目录 │ ├─controller 控制器目录 │ ├─model 模型目录 │ ├─view 视图目......

    2024-03-31
    8280