简体   繁体   English

允许 PHP 脚本访问文件夹中的 PDF - 但阻止直接 URL 引用

[英]Allow a PHP script access to PDFs in a folder - but prevent direct URL references

On a godaddy hosted website using CPanel, I have a small PHP script that shows each line in a text file that's on the server.在使用 CPanel 的 Godaddy 托管网站上,我有一个小的 PHP 脚本,它显示服务器上文本文件中的每一行。 Each line contains a private href link to a PDF that only the logged-in user can see.每行包含一个指向 PDF 的私有 href 链接,只有登录用户才能看到。 The links points to various PDFs in the same folder on the server.这些链接指向服务器上同一文件夹中的各种 PDF。 The code works fine and I can click on the link and see each PDF.代码运行良好,我可以单击链接并查看每个 PDF。

The problem is that each PDF can also be seen by using a direct URL query (ie website/folder/pdfname.pdf).问题是每个 PDF 也可以通过使用直接 URL 查询(即网站/文件夹/pdfname.pdf)来查看。 As these are private PDFs, I don't want them public.由于这些是私人 PDF,我不希望它们公开。 I've tried changing CPanel permissions on the folder to "owner" - but that seems to prevent the PHP script from opening the PDFs also.我已经尝试将文件夹上的 CPanel 权限更改为“所有者”——但这似乎也阻止了 PHP 脚本打开 PDF。

Is there a way to allow a PHP script access to PDFs in a folder - but prevent direct URL references?有没有办法允许 PHP 脚本访问文件夹中的 PDF - 但防止直接 URL 引用?

NOTE: I'm not particularly adept at PHP or CPanel - sorry.注意:我不是特别擅长 PHP 或 CPanel - 抱歉。

Code...代码...

$fname = "PDF-" . $user_name.".txt";
$fnum = fopen($fname,"r");
echo "<tr>";
While (($str = fgets($fnum)) !==  false) {
    $arr = explode("|",$str);   
    for ($x  = 0 ;  $x < count($arr);  $x++) {
        echo "<td>$arr[$x]</td>";
    }
    echo "</tr>";   
}
echo "</tr>";
fclose($fnum);

File contents...文件内容...

Xyz Company|21 Jan 2018|<a href="http://website.com"> website link</a>
Xyz Company|21 Jan 2018|<a href="http://website.com"> website link</a>
Xyz Company|21 Jan 2018|<a href="http://website.com"> website link</a>
Xyz Company|21 Jan 2018|<a href="http://website.com"> website link</a>*

Asside from removing the files from the root, if you are running apache, you can change your .htaccess (I'm sure windows-based system have a web.config equivalent) to forbid access to certain files directly.除了从根目录中删除文件之外,如果您正在运行 apache,您还可以更改.htaccess (我确信基于 Windows 的系统具有web.config等效项)以禁止直接访问某些文件。 If you add this snippet to that file, it will deny files with .pdf extension:如果将此代码段添加到该文件,它将拒绝扩展名为.pdf文件:

<FilesMatch "\.(pdf)$">
Order Allow,Deny
Deny from all
</FilesMatch>

From there, inside your app, you can create some sort of system for curating your PDF links, so if you store the real path in a database and use the id as the link similar to:从那里,在您的应用程序中,您可以创建某种系统来管理您的 PDF 链接,因此如果您将真实路径存储在数据库中并使用 id 作为类似于以下内容的链接:

http://www.example.com/?file=1

or if you just do a simple scan:或者如果你只是做一个简单的扫描:

<?php
# The folder that the PDFs are in
$dir = __DIR__.'/website/folder/';
# Loop over a scan of the directory (you can also use glob() here)
foreach(scandir($dir) as $file):
    # If file, create a link
    if(is_file($dir.$file)): ?>
<a href="?action=download&file=<?php echo $file ?>"><?php echo $file ?></a>
<?php
    endif;
endforeach;

Then, if the user tries to download using the link, you check they are first logged in and if they are, download the file by doing a script like so BEFORE you output anything else to the browser (including spaces) :然后,如果用户尝试使用链接下载,您检查他们是否首先登录,如果他们是,请在向浏览器输出任何其他内容(包括空格)之前通过执行如下脚本下载文件:

<?php
session_start();
# First check that the user is logged in
if(empty($_SESSION['username']))
    die('You must be logged in to download this document.');
# Not sure which directory you are currently in, so I will assume root
# I would do basename() here incase the user tries to add in something like:
# ../index.php and tries to download files they are not supposed to
$file = __DIR__.'/website/folder/'.basename($_GET['file']);
if(!is_file($file))
    die('File does not exist.');
# Double check that the file is a pdf
elseif(strtolower(pathinfo($file, PATHINFO_EXTENSION)) != 'pdf')
    die('File appears to be invalid.');
# Start download headers
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;

One simpler and basic example (and derivative of previous answer) is to use two separate PHP files , where one is evaluating a set cookie (set to expire soon) in the browser upon link click (set via JS or PHP or other).一个更简单和基本的示例(以及先前答案的派生)是使用两个单独的 PHP 文件,其中一个是在点击链接(通过 JS 或 PHP 或其他设置)时在浏览器中评估设置的 cookie(设置为即将过期)。 If the cookie was read correctly, the first PHP page imports a second page that utilizes the PHP header() redirect containing your original file name forcibly downloaded with another name .如果 cookie 被正确读取,第一个 PHP 页面会导入第二个页面,该页面利用 PHP header()重定向,其中包含您用另一个名称强行下载的原始文件 Using the Content Disposition header field.使用内容处置标头字段。

In action this works like this在行动中这是这样工作的

1: Original page with download links - we set the cookie to work for 2 minutes 1:带有下载链接的原始页面 - 我们将 cookie 设置为工作 2 分钟

<a onclick="setCookie(1, 1, 2, 60)" href="php-secure-files-delivery-page.php">Download My Final PDF name.pdf</a>

<script type="text/javascript">

    // set a cookie with your own time limits.
    function setCookie(days, hours, minutes, seconds) { // Create cookie
        var expires;
        var date = new Date();
        date.setTime(date.getTime()+(days*hours*minutes*seconds*1000));
        expires = "; expires="+date.toGMTString();
        document.cookie = "my_cookie_name"+"="+"my_cookie_value"+expires+"; path=/";
    }

</script>

On the link page we include a hyperlink with the evaluating PHP page.在链接页面上,我们包含一个带有评估 PHP 页面的超链接。 Here we use JavaScript to set a cookie using the custom function setCookie(days, hours, minutes, seconds) , that will receive your wishes for expiry.在这里,我们使用 JavaScript 使用自定义函数setCookie(days, hours, minutes, seconds)设置 cookie,它将接收您的到期愿望。 Just note that 1 is the minimum number.请注意, 1是最小数字。 Not 0 .不是0

2: Download page - evaluating cookie and presenting texts, or simply downloading the file 2: 下载页面 - 评估 cookie 和呈现文本,或简单地下载文件

(php-secure-files-delivery-page.php) (php-secure-files-delivery-page.php)

<?php

// if the cookie is set correctly, load the file downloader page.

if (isset($_COOKIE['my_cookie_name'] && $_COOKIE['my_cookie_name'] === 'my_cookie_value')) {

    require_once 'file-downloader.php'; // the file will force the download upon import.

} else {
    
    die('The link expired, go to your downloads section and click on the link again.');
}

?>

Here we evaluate the cookie, present either the correct info or die().在这里,我们评估 cookie,显示正确的信息或 die()。 Using require_once we get the PHP page into the current one.使用require_once我们让 PHP 页面进入当前页面。

3: Imported file includer PHP page 3:导入文件includer PHP页面

(file-downloader.php) (文件下载器.php)

<?php

// We'll be outputting a PDF
header('Content-Type: application/pdf');

// It will be downloaded as your-downloaded.pdf
header('Content-Disposition: attachment; filename="your-downloaded.pdf"');

// The PDF source is in your own specified long name
readfile('original-with-really-weird-original-name.pdf');

?>

Results结果

  • User always go to the same page, being presented with the appropriate information.用户总是转到同一页面,并看到适当的信息。
  • You can name your original files on your server anything you want, like "my_really_difficult_and_long_file_name.pdf", while the user sees only the nice pretty file name when the file is downloaded.您可以随意命名服务器上的原始文件,例如“my_really_difficult_and_long_file_name.pdf”,而用户在下载文件时只会看到漂亮的文件名。
  • for more files, use an extra input in the cookie function to take the file name too, and some if statements in the php downloader page, that looks for separate end PHP pages to require_once .对于更多文件,在 cookie 函数中使用额外的输入来获取文件名,并在 php 下载器页面中使用一些 if 语句,用于寻找单独的 PHP 页面到require_once
  • If you go to the browsers "Downloads" section to try to get the url of the downloaded file, you see the initiating PHP page, the second page, that leaves you empty with a die() if no correct cookie was set.如果您转到浏览器的“下载”部分以尝试获取下载文件的 url,您会看到启动 PHP 页面,即第二个页面,如果没有设置正确的 cookie,则该页面会显示为die() That cookie is only set when you want it to.该 cookie 仅在您需要时设置。 On your pages.在您的页面上。 You can of course do this in JavaScript too, but that will expose the cookie, still, for most unauthorized sharing, that takes care of it.当然,您也可以在 JavaScript 中执行此操作,但这会暴露 cookie,但对于大多数未经授权的共享,它仍然会处理它。

Lastly, easy security for your folder (without Apache/Nginx/.htaccess stuff)最后,轻松保护您的文件夹(没有 Apache/Nginx/.htaccess 的东西)

Using .htaccess files on local folders or directives on your server is the best and most secure way.在服务器上的本地文件夹或指令中使用 .htaccess 文件是最好和最安全的方式。 But that´s not transferable to your other applications on other systems.但这不能转移到其他系统上的其他应用程序。 Instead use a index.php and a default.php page on your PDF file´s parent folder, where they are located, including this header redirect to wear off unwanted visits:而是在您的 PDF 文件的父文件夹上使用index.phpdefault.php页面,它们所在的位置,包括此标题重定向以消除不需要的访问:

<?php
    header("Location: http://yoursite.com/some-other-page/"); /* Redirect browser here */
?>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM