关于apache:如何防止非会员用PHP直接访问私有内容

How to prevent non members from directly accessing private content with PHP

我知道 .htaccess Apache 保护文件夹免受非成员访问的方法,但我想知道如何仅使用 PHP 来完成。

检查会话变量可以保护 php 页面,但直接访问照片和/或 pdf 似乎有时会被忽略。例如,如果我从 facebook 复制照片的直接链接,注销,然后将我的浏览器定向到照片的 url,它显示没有问题。

Rapidshare 等网站如何防止对其内容的直接访问?

我知道有一种方法可以在 Apache 中设置变量。也许有一种方法可以使用 Apache 检查会话变量?


从问题和评论来看,您似乎在寻找看起来像直接访问的 URL,但通过 PHP 脚本允许授权下载它们。我不知道你当前的目录结构,所以我要为这个答案的测试用例做一个——尽管是一个小清单:

1
2
3
4
5
public-html/
    content/        <- where the actual files are
        .htaccess   <- to deny access to the directory
    .htaccess       <- the one created here
    download.php    <- download script

由此,我们将假设所有文件都位于 /content 中,并且"直接链接"看起来会指向 /file

/content/.htaccess:

1
deny from all

下一个带有注释的第一个 RewriteRule 是为了使已经存在的普通文件不会被覆盖,而前面的 .htaccess 防止该目录中的文件被访问。如果您有一个已经在使用的 .htaccess,则重要的规则是最后一个,如果匹配,它将停止处理,因此将其相应地放置在您现有的 .htaccess 中。

/.htaccess:

1
2
3
4
5
6
7
8
9
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} -s [OR]
    RewriteCond %{REQUEST_FILENAME} -l [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^.*$ - [NC,L]

    RewriteRule ^file/(.+)$ download.php?file=$1 [L]
</IfModule>

有了它,如果转到 /file/myDocument.pdf,它看起来就像直接访问一个文件,它会像 download.php?file=myDocument.pdf 一样被发送到脚本。

从那里开始,创建要使用的下载脚本。我要发布的是来自 Armand Niculescu 的修改版本。我只在此处发布相关部分(全部内容可在 PasteBin 上找到,您可以修改此处发布的部分以满足您的需要):

1
2
3
4
5
6
7
8
9
/******************************
 * PERFORM AUTHORIZATION HERE *
 *   The following is added   *
 ******************************/

if($_SESSION['validated'] !== true) {
    //Not authorized to download, send header and exit
    header("HTTP/1.0 401 Unauthorized");
    exit;
}

我还对正确的文件路径进行了另一个小编辑,向下 8 行:

1
$file_path  = './content/' . $file_name;

到此结束这个关于如何获得工作下载脚本并使其看起来像直接文件访问的小示例。


从您的问题中,我了解到您已经知道如何保护文件夹不被直接访问以及如何保护 php 脚本。所以你需要的是一个受保护的 php 脚本,提供对文件的间接访问(正如评论中已经提到的那样)。

在创建这个下载脚本时,你应该记住一些重要的细节:

  • 提供 mime 类型。在许多情况下 header("Content-Type: application/octet-stream") 就足够了,尽管
  • 告诉客户端下载文件(而不是显示它)并提供文件名:header('Content-Disposition: attachment; filename="' . basename($path) . '"')
  • 提供文件大小:header("Content-Length:" . filesize($path));
  • 为了避免大文件出现问题,你应该使用类似下面的东西而不是 readfile:

    1
    2
    3
    4
    5
    6
    7
    $file = fopen($path, 'r');
    flush();
      while (!feof($file)) {
      print(fread($file, 4096));
      flush();
    }
    fclose($file);