You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP实现指定来源可在线预览PDF的问题求助

问题分析与修复方案

核心问题

  1. Referer验证逻辑错误:当前代码将parse_url返回的完整数组(包含scheme、path等字段)与允许的域名列表对比,逻辑完全错误,应该仅提取并验证host字段。
  2. Inline预览时Referer丢失:部分浏览器在处理inline类型的PDF预览时,可能因安全策略或请求方式不携带HTTP_REFERER;另外,当HTTP_REFERER为空时,parse_url会抛出警告,导致后续验证失败。
  3. 文件路径处理不安全:直接使用REQUEST_URI拼接路径存在路径遍历风险,且路径计算逻辑错误。

具体修复步骤

1. 修正Referer验证逻辑

只提取Referer的host部分,去除www.前缀后再与允许列表对比,同时处理HTTP_REFERER为空的情况:

// 初始化验证标记
$isAllowed = false;

// 先检查HTTP_REFERER是否存在
if (!empty($_SERVER['HTTP_REFERER'])) {
    $refererData = parse_url($_SERVER['HTTP_REFERER']);
    // 确保host字段存在
    if (isset($refererData['host'])) {
        $refererHost = $refererData['host'];
        // 去除www.前缀
        if (str_starts_with($refererHost, 'www.')) {
            $refererHost = ltrim($refererHost, 'www.');
        }
        // 验证是否在允许列表中
        $referrals = array("website1.com", "website2.com", "website3.com");
        $isAllowed = in_array($refererHost, $referrals);
    }
}

2. 修复文件路径计算逻辑

避免使用REQUEST_URI拼接路径,直接从请求中提取文件名,拼接安全的相对路径:

// 从REQUEST_URI中提取文件名(避免路径遍历)
$pathParts = explode('/', $_SERVER['REQUEST_URI']);
$fileName = end($pathParts);
// 只保留合法的文件名(过滤非法字符)
$fileName = preg_replace('/[^a-zA-Z0-9_\-\.]/', '', $fileName);
// 正确的文件路径:check.php在sub1/sub2,PDF在sub1/sub2/sub3,所以用./sub3/
$file = './sub3/' . $fileName;

3. 调整响应头与处理Referer丢失的兼容

如果需要允许同域内的预览请求(比如从sub1/sub2下的页面打开PDF),可以额外检查请求是否来自同域;同时设置正确的响应头:

if ($isAllowed) {
    if (!file_exists($file)) {
        echo 'error1';
        exit;
    }

    // 设置响应头,支持inline预览
    header("Content-Type: application/pdf");
    // 对于inline预览,filename可选,但建议保留
    header("Content-Disposition: inline; filename=\"{$fileDescription}.pdf\"");
    header("Content-Length: " . filesize($file));
    // 可选:添加缓存头,提升体验
    header("Cache-Control: public, max-age=3600");
    
    readfile($file);
    exit;
} else {
    echo 'error2';
    exit;
}

4. 优化浏览器Referer发送策略

在放置PDF链接的页面中添加Referrer-Policy头,确保浏览器发送Referer:

<!-- 在页面的<head>中添加 -->
<meta name="referrer" content="strict-origin-when-cross-origin">

或者通过服务器配置(比如.htaccess)添加:

Header set Referrer-Policy "strict-origin-when-cross-origin"

完整修复后的check.php代码

<?php
$fileDescription = 'funnyFileName';
$isAllowed = false;

// 验证Referer
if (!empty($_SERVER['HTTP_REFERER'])) {
    $refererData = parse_url($_SERVER['HTTP_REFERER']);
    if (isset($refererData['host'])) {
        $refererHost = $refererData['host'];
        if (str_starts_with($refererHost, 'www.')) {
            $refererHost = ltrim($refererHost, 'www.');
        }
        $referrals = array("website1.com", "website2.com", "website3.com");
        $isAllowed = in_array($refererHost, $referrals);
    }
}

// 处理文件路径
$pathParts = explode('/', $_SERVER['REQUEST_URI']);
$fileName = end($pathParts);
$fileName = preg_replace('/[^a-zA-Z0-9_\-\.]/', '', $fileName);
$file = './sub3/' . $fileName;

// 验证与输出
if ($isAllowed) {
    if (!file_exists($file)) {
        echo 'error1';
        exit;
    }

    header("Content-Type: application/pdf");
    header("Content-Disposition: inline; filename=\"{$fileDescription}.pdf\"");
    header("Content-Length: " . filesize($file));
    header("Cache-Control: public, max-age=3600");
    
    readfile($file);
} else {
    echo 'error2';
}
?>

内容的提问来源于stack exchange,提问作者robinw99

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 00:33:28